mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-01 10:13:30 +00:00
Core - Layer refactor WIP
This commit is contained in:
parent
be897b99f7
commit
d37420e462
@ -2,13 +2,23 @@
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public class LayerPropertyEventArgs : EventArgs
|
public class LayerPropertyEventArgs<T> : EventArgs
|
||||||
{
|
{
|
||||||
public LayerPropertyEventArgs(BaseLayerProperty layerProperty)
|
public LayerPropertyEventArgs(LayerProperty<T> layerProperty)
|
||||||
{
|
{
|
||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
public LayerProperty<T> LayerProperty { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LayerPropertyEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public LayerPropertyEventArgs(ILayerProperty layerProperty)
|
||||||
|
{
|
||||||
|
LayerProperty = layerProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILayerProperty LayerProperty { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
src/Artemis.Core/Models/IStorageModel.cs
Normal file
18
src/Artemis.Core/Models/IStorageModel.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model that can be loaded and saved to persistent storage
|
||||||
|
/// </summary>
|
||||||
|
public interface IStorageModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the model from its associated entity
|
||||||
|
/// </summary>
|
||||||
|
void Load();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the model to its associated entity
|
||||||
|
/// </summary>
|
||||||
|
void Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/Artemis.Core/Models/IUpdateModel.cs
Normal file
14
src/Artemis.Core/Models/IUpdateModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a model that updates using a delta time
|
||||||
|
/// </summary>
|
||||||
|
public interface IUpdateModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Performs an update on the model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">The delta time in seconds</param>
|
||||||
|
void Update(double deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,46 +3,51 @@
|
|||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class FloatDataBindingConverter : DataBindingConverter
|
public class FloatDataBindingConverter : FloatDataBindingConverter<float>
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <typeparam name="T">The type of layer property this converter is applied to</typeparam>
|
||||||
|
public class FloatDataBindingConverter<T> : DataBindingConverter<T, float>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="FloatDataBindingConverter{T}" /> class
|
||||||
|
/// </summary>
|
||||||
public FloatDataBindingConverter()
|
public FloatDataBindingConverter()
|
||||||
{
|
{
|
||||||
SupportedType = typeof(float);
|
|
||||||
SupportsSum = true;
|
SupportsSum = true;
|
||||||
SupportsInterpolate = true;
|
SupportsInterpolate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Sum(object a, object b)
|
public override float Sum(float a, float b)
|
||||||
{
|
{
|
||||||
return Convert.ToSingle(a) + Convert.ToSingle(b);
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Interpolate(object a, object b, double progress)
|
public override float Interpolate(float a, float b, double progress)
|
||||||
{
|
{
|
||||||
var floatA = Convert.ToSingle(a);
|
var diff = a - b;
|
||||||
var floatB = Convert.ToSingle(b);
|
return (float) (a + diff * progress);
|
||||||
var diff = floatB - floatA;
|
|
||||||
return floatA + diff * progress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ApplyValue(object value)
|
public override void ApplyValue(float value)
|
||||||
{
|
{
|
||||||
var floatValue = Convert.ToSingle(value);
|
|
||||||
if (DataBinding.LayerProperty.PropertyDescription.MaxInputValue is float max)
|
if (DataBinding.LayerProperty.PropertyDescription.MaxInputValue is float max)
|
||||||
floatValue = Math.Min(floatValue, max);
|
value = Math.Min(value, max);
|
||||||
if (DataBinding.LayerProperty.PropertyDescription.MinInputValue is float min)
|
if (DataBinding.LayerProperty.PropertyDescription.MinInputValue is float min)
|
||||||
floatValue = Math.Max(floatValue, min);
|
value = Math.Max(value, min);
|
||||||
|
|
||||||
ValueSetter?.Invoke(floatValue);
|
ValueSetter?.Invoke(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object GetValue()
|
public override float GetValue()
|
||||||
{
|
{
|
||||||
return ValueGetter?.Invoke();
|
return ValueGetter?.Invoke() ?? 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,11 +3,10 @@
|
|||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class GeneralDataBindingConverter : DataBindingConverter
|
public class GeneralDataBindingConverter<T> : DataBindingConverter<T, object> where T : ILayerProperty
|
||||||
{
|
{
|
||||||
public GeneralDataBindingConverter()
|
public GeneralDataBindingConverter()
|
||||||
{
|
{
|
||||||
SupportedType = typeof(object);
|
|
||||||
SupportsSum = false;
|
SupportsSum = false;
|
||||||
SupportsInterpolate = false;
|
SupportsInterpolate = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,18 @@
|
|||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class IntDataBindingConverter : DataBindingConverter
|
public class IntDataBindingConverter : FloatDataBindingConverter<int>
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class IntDataBindingConverter<T> : DataBindingConverter<T, int> where T : ILayerProperty
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="IntDataBindingConverter{T}" /> class
|
||||||
|
/// </summary>
|
||||||
public IntDataBindingConverter()
|
public IntDataBindingConverter()
|
||||||
{
|
{
|
||||||
SupportedType = typeof(int);
|
|
||||||
SupportsSum = true;
|
SupportsSum = true;
|
||||||
SupportsInterpolate = true;
|
SupportsInterpolate = true;
|
||||||
}
|
}
|
||||||
@ -19,30 +26,28 @@ namespace Artemis.Core
|
|||||||
public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero;
|
public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Sum(object a, object b)
|
public override int Sum(int a, int b)
|
||||||
{
|
{
|
||||||
return (int) a + (int) b;
|
return a + b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object Interpolate(object a, object b, double progress)
|
public override int Interpolate(int a, int b, double progress)
|
||||||
{
|
{
|
||||||
var intA = (int) a;
|
var diff = b - a;
|
||||||
var intB = (int) b;
|
return (int) Math.Round(a + diff * progress, InterpolationRoundingMode);
|
||||||
var diff = intB - intA;
|
|
||||||
return (int) Math.Round(intA + diff * progress, InterpolationRoundingMode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void ApplyValue(object value)
|
public override void ApplyValue(int value)
|
||||||
{
|
{
|
||||||
ValueSetter?.Invoke(value);
|
ValueSetter?.Invoke(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override object GetValue()
|
public override int GetValue()
|
||||||
{
|
{
|
||||||
return ValueGetter?.Invoke();
|
return ValueGetter?.Invoke() ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
// This is internal because it's mainly a proof-of-concept
|
||||||
|
internal class SKColorArgbDataBindingConverter : DataBindingConverter<SKColor, byte>
|
||||||
|
{
|
||||||
|
private readonly Channel _channel;
|
||||||
|
|
||||||
|
public SKColorArgbDataBindingConverter(Channel channel)
|
||||||
|
{
|
||||||
|
_channel = channel;
|
||||||
|
|
||||||
|
SupportsSum = true;
|
||||||
|
SupportsInterpolate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte Sum(byte a, byte b)
|
||||||
|
{
|
||||||
|
return ClampToByte(a + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte Interpolate(byte a, byte b, double progress)
|
||||||
|
{
|
||||||
|
var diff = b - a;
|
||||||
|
return ClampToByte(diff * progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ApplyValue(byte value)
|
||||||
|
{
|
||||||
|
switch (_channel)
|
||||||
|
{
|
||||||
|
case Channel.Alpha:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithAlpha(value);
|
||||||
|
break;
|
||||||
|
case Channel.Red:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithRed(value);
|
||||||
|
break;
|
||||||
|
case Channel.Green:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithGreen(value);
|
||||||
|
break;
|
||||||
|
case Channel.Blue:
|
||||||
|
DataBinding.LayerProperty.CurrentValue = DataBinding.LayerProperty.CurrentValue.WithBlue(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte GetValue()
|
||||||
|
{
|
||||||
|
switch (_channel)
|
||||||
|
{
|
||||||
|
case Channel.Alpha:
|
||||||
|
return DataBinding.LayerProperty.CurrentValue.Alpha;
|
||||||
|
case Channel.Red:
|
||||||
|
return DataBinding.LayerProperty.CurrentValue.Red;
|
||||||
|
case Channel.Green:
|
||||||
|
return DataBinding.LayerProperty.CurrentValue.Green;
|
||||||
|
case Channel.Blue:
|
||||||
|
return DataBinding.LayerProperty.CurrentValue.Blue;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte ClampToByte(double value)
|
||||||
|
{
|
||||||
|
return (byte) Math.Clamp(value, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Channel
|
||||||
|
{
|
||||||
|
Alpha,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,95 +0,0 @@
|
|||||||
using System;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
// This is internal because it's mainly a proof-of-concept
|
|
||||||
internal class SKColorPartDataBindingConverter : DataBindingConverter
|
|
||||||
{
|
|
||||||
private readonly Channel _channel;
|
|
||||||
|
|
||||||
public SKColorPartDataBindingConverter(Channel channel)
|
|
||||||
{
|
|
||||||
_channel = channel;
|
|
||||||
|
|
||||||
SupportsSum = true;
|
|
||||||
SupportsInterpolate = true;
|
|
||||||
SupportedType = _channel switch
|
|
||||||
{
|
|
||||||
Channel.Alpha => typeof(byte),
|
|
||||||
Channel.Red => typeof(byte),
|
|
||||||
Channel.Green => typeof(byte),
|
|
||||||
Channel.Blue => typeof(byte),
|
|
||||||
Channel.Hue => typeof(float),
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Sum(object a, object b)
|
|
||||||
{
|
|
||||||
return (float) a + (float) b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Interpolate(object a, object b, double progress)
|
|
||||||
{
|
|
||||||
var diff = (float) b - (float) a;
|
|
||||||
return diff * progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ApplyValue(object value)
|
|
||||||
{
|
|
||||||
var property = (SKColorLayerProperty) DataBinding.LayerProperty;
|
|
||||||
switch (_channel)
|
|
||||||
{
|
|
||||||
case Channel.Alpha:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithAlpha((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Red:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithRed((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Green:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithGreen((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Blue:
|
|
||||||
property.CurrentValue = property.CurrentValue.WithBlue((byte) value);
|
|
||||||
break;
|
|
||||||
case Channel.Hue:
|
|
||||||
property.CurrentValue.ToHsv(out var h, out var s, out var v);
|
|
||||||
property.CurrentValue = SKColor.FromHsv((float) value, s, v);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object GetValue()
|
|
||||||
{
|
|
||||||
var property = (SKColorLayerProperty) DataBinding.LayerProperty;
|
|
||||||
switch (_channel)
|
|
||||||
{
|
|
||||||
case Channel.Alpha:
|
|
||||||
return property.CurrentValue.Alpha;
|
|
||||||
case Channel.Red:
|
|
||||||
return property.CurrentValue.Red;
|
|
||||||
case Channel.Green:
|
|
||||||
return property.CurrentValue.Green;
|
|
||||||
case Channel.Blue:
|
|
||||||
return property.CurrentValue.Blue;
|
|
||||||
case Channel.Hue:
|
|
||||||
property.CurrentValue.ToHsv(out var h, out _, out _);
|
|
||||||
return h;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Channel
|
|
||||||
{
|
|
||||||
Alpha,
|
|
||||||
Red,
|
|
||||||
Green,
|
|
||||||
Blue,
|
|
||||||
Hue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,48 +9,46 @@ using Artemis.Storage.Entities.Profile.DataBindings;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// A data binding that binds a certain <see cref="BaseLayerProperty" /> to a value inside a <see cref="DataModel" />
|
public class DataBinding<TLayerProperty, TProperty> : IDataBinding
|
||||||
/// </summary>
|
|
||||||
public class DataBinding
|
|
||||||
{
|
{
|
||||||
private readonly List<DataBindingModifier> _modifiers = new List<DataBindingModifier>();
|
private readonly List<DataBindingModifier<TLayerProperty, TProperty>> _modifiers = new List<DataBindingModifier<TLayerProperty, TProperty>>();
|
||||||
|
|
||||||
private object _currentValue;
|
private TProperty _currentValue;
|
||||||
private object _previousValue;
|
|
||||||
private TimeSpan _easingProgress;
|
private TimeSpan _easingProgress;
|
||||||
|
private TProperty _previousValue;
|
||||||
|
|
||||||
internal DataBinding(DataBindingRegistration dataBindingRegistration)
|
internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
|
||||||
{
|
{
|
||||||
LayerProperty = dataBindingRegistration.LayerProperty;
|
LayerProperty = dataBindingRegistration.LayerProperty;
|
||||||
Entity = new DataBindingEntity();
|
Entity = new DataBindingEntity();
|
||||||
|
|
||||||
ApplyRegistration(dataBindingRegistration);
|
ApplyRegistration(dataBindingRegistration);
|
||||||
ApplyToEntity();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataBinding(BaseLayerProperty layerProperty, DataBindingEntity entity)
|
internal DataBinding(LayerProperty<TLayerProperty> layerProperty, DataBindingEntity entity)
|
||||||
{
|
{
|
||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
|
||||||
ApplyToDataBinding();
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the layer property this data binding targets
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data binding registration this data binding is based upon
|
/// Gets the data binding registration this data binding is based upon
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBindingRegistration Registration { get; private set; }
|
public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the layer property this data binding targets
|
||||||
|
/// </summary>
|
||||||
|
public LayerProperty<TLayerProperty> LayerProperty { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the converter used to apply this data binding to the <see cref="LayerProperty" />
|
/// Gets the converter used to apply this data binding to the <see cref="LayerProperty" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBindingConverter Converter { get; private set; }
|
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the property on the <see cref="LayerProperty" /> this data binding targets
|
/// Gets the property on the <see cref="LayerProperty" /> this data binding targets
|
||||||
@ -82,7 +80,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list of modifiers applied to this data binding
|
/// Gets a list of modifiers applied to this data binding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<DataBindingModifier> Modifiers => _modifiers.AsReadOnly();
|
public IReadOnlyList<DataBindingModifier<TLayerProperty, TProperty>> Modifiers => _modifiers.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the compiled function that gets the current value of the data binding target
|
/// Gets the compiled function that gets the current value of the data binding target
|
||||||
@ -91,10 +89,24 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal DataBindingEntity Entity { get; }
|
internal DataBindingEntity Entity { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the smoothing progress of the data binding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">The time in seconds that passed since the last update</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
// Data bindings cannot go back in time like brushes
|
||||||
|
deltaTime = Math.Max(0, deltaTime);
|
||||||
|
|
||||||
|
_easingProgress = _easingProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
||||||
|
if (_easingProgress > EasingTime)
|
||||||
|
_easingProgress = EasingTime;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a modifier to the data binding's <see cref="Modifiers" /> collection
|
/// Adds a modifier to the data binding's <see cref="Modifiers" /> collection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AddModifier(DataBindingModifier modifier)
|
public void AddModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
|
||||||
{
|
{
|
||||||
if (!_modifiers.Contains(modifier))
|
if (!_modifiers.Contains(modifier))
|
||||||
{
|
{
|
||||||
@ -108,7 +120,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a modifier from the data binding's <see cref="Modifiers" /> collection
|
/// Removes a modifier from the data binding's <see cref="Modifiers" /> collection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveModifier(DataBindingModifier modifier)
|
public void RemoveModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
|
||||||
{
|
{
|
||||||
if (_modifiers.Contains(modifier))
|
if (_modifiers.Contains(modifier))
|
||||||
{
|
{
|
||||||
@ -147,7 +159,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
|
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public object GetValue(object baseValue)
|
public TProperty GetValue(TProperty baseValue)
|
||||||
{
|
{
|
||||||
if (CompiledTargetAccessor == null || Converter == null)
|
if (CompiledTargetAccessor == null || Converter == null)
|
||||||
return baseValue;
|
return baseValue;
|
||||||
@ -156,7 +168,7 @@ namespace Artemis.Core
|
|||||||
foreach (var dataBindingModifier in Modifiers)
|
foreach (var dataBindingModifier in Modifiers)
|
||||||
dataBindingValue = dataBindingModifier.Apply(dataBindingValue);
|
dataBindingValue = dataBindingModifier.Apply(dataBindingValue);
|
||||||
|
|
||||||
var value = Convert.ChangeType(dataBindingValue, TargetProperty.PropertyType);
|
var value = (TProperty) Convert.ChangeType(dataBindingValue, typeof(TProperty));
|
||||||
|
|
||||||
// If no easing is to be applied simple return whatever the current value is
|
// If no easing is to be applied simple return whatever the current value is
|
||||||
if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
|
if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
|
||||||
@ -170,13 +182,6 @@ namespace Artemis.Core
|
|||||||
return GetInterpolatedValue();
|
return GetInterpolatedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetEasing(object value)
|
|
||||||
{
|
|
||||||
_previousValue = GetInterpolatedValue();
|
|
||||||
_currentValue = value;
|
|
||||||
_easingProgress = TimeSpan.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the type of the target property of this data binding
|
/// Returns the type of the target property of this data binding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -193,24 +198,8 @@ namespace Artemis.Core
|
|||||||
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
|
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Updates the smoothing progress of the data binding
|
public void Apply()
|
||||||
/// </summary>
|
|
||||||
/// <param name="deltaTime">The time in seconds that passed since the last update</param>
|
|
||||||
public void Update(double deltaTime)
|
|
||||||
{
|
|
||||||
// Data bindings cannot go back in time like brushes
|
|
||||||
deltaTime = Math.Max(0, deltaTime);
|
|
||||||
|
|
||||||
_easingProgress = _easingProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
|
||||||
if (_easingProgress > EasingTime)
|
|
||||||
_easingProgress = EasingTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the value on the <see cref="LayerProperty" /> according to the data binding
|
|
||||||
/// </summary>
|
|
||||||
public void ApplyToProperty()
|
|
||||||
{
|
{
|
||||||
if (Converter == null)
|
if (Converter == null)
|
||||||
return;
|
return;
|
||||||
@ -219,44 +208,8 @@ namespace Artemis.Core
|
|||||||
Converter.ApplyValue(GetValue(value));
|
Converter.ApplyValue(GetValue(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
/// <inheritdoc />
|
||||||
{
|
public void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
||||||
// General
|
|
||||||
Entity.TargetProperty = TargetProperty?.Name;
|
|
||||||
Entity.DataBindingMode = (int) Mode;
|
|
||||||
Entity.EasingTime = EasingTime;
|
|
||||||
Entity.EasingFunction = (int) EasingFunction;
|
|
||||||
|
|
||||||
// Data model
|
|
||||||
Entity.SourceDataModelGuid = SourceDataModel?.PluginInfo?.Guid;
|
|
||||||
Entity.SourcePropertyPath = SourcePropertyPath;
|
|
||||||
|
|
||||||
// Modifiers
|
|
||||||
Entity.Modifiers.Clear();
|
|
||||||
foreach (var dataBindingModifier in Modifiers)
|
|
||||||
{
|
|
||||||
dataBindingModifier.ApplyToEntity();
|
|
||||||
Entity.Modifiers.Add(dataBindingModifier.Entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyToDataBinding()
|
|
||||||
{
|
|
||||||
// General
|
|
||||||
ApplyRegistration(LayerProperty.DataBindingRegistrations.FirstOrDefault(p => p.Property.Name == Entity.TargetProperty));
|
|
||||||
|
|
||||||
Mode = (DataBindingMode) Entity.DataBindingMode;
|
|
||||||
EasingTime = Entity.EasingTime;
|
|
||||||
EasingFunction = (Easings.Functions) Entity.EasingFunction;
|
|
||||||
|
|
||||||
// Data model is done during Initialize
|
|
||||||
|
|
||||||
// Modifiers
|
|
||||||
foreach (var dataBindingModifierEntity in Entity.Modifiers)
|
|
||||||
_modifiers.Add(new DataBindingModifier(this, dataBindingModifierEntity));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
|
||||||
{
|
{
|
||||||
// Source
|
// Source
|
||||||
if (Entity.SourceDataModelGuid != null && SourceDataModel == null)
|
if (Entity.SourceDataModelGuid != null && SourceDataModel == null)
|
||||||
@ -271,7 +224,14 @@ namespace Artemis.Core
|
|||||||
dataBindingModifier.Initialize(dataModelService, dataBindingService);
|
dataBindingModifier.Initialize(dataModelService, dataBindingService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyRegistration(DataBindingRegistration dataBindingRegistration)
|
private void ResetEasing(TProperty value)
|
||||||
|
{
|
||||||
|
_previousValue = GetInterpolatedValue();
|
||||||
|
_currentValue = value;
|
||||||
|
_easingProgress = TimeSpan.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyRegistration(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
|
||||||
{
|
{
|
||||||
if (dataBindingRegistration != null)
|
if (dataBindingRegistration != null)
|
||||||
dataBindingRegistration.DataBinding = this;
|
dataBindingRegistration.DataBinding = this;
|
||||||
@ -283,15 +243,15 @@ namespace Artemis.Core
|
|||||||
if (GetTargetType().IsValueType)
|
if (GetTargetType().IsValueType)
|
||||||
{
|
{
|
||||||
if (_currentValue == null)
|
if (_currentValue == null)
|
||||||
_currentValue = GetTargetType().GetDefault();
|
_currentValue = default;
|
||||||
if (_previousValue == null)
|
if (_previousValue == null)
|
||||||
_previousValue = _currentValue;
|
_previousValue = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
Converter?.Initialize(this);
|
Converter?.Initialize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private object GetInterpolatedValue()
|
private TProperty GetInterpolatedValue()
|
||||||
{
|
{
|
||||||
if (_easingProgress == EasingTime || !Converter.SupportsInterpolate)
|
if (_easingProgress == EasingTime || !Converter.SupportsInterpolate)
|
||||||
return _currentValue;
|
return _currentValue;
|
||||||
@ -318,6 +278,50 @@ namespace Artemis.Core
|
|||||||
CompiledTargetAccessor = lambda.Compile();
|
CompiledTargetAccessor = lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Storage
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
// General
|
||||||
|
var registration = LayerProperty.GetDataBindingRegistration<TProperty>(Entity.TargetProperty);
|
||||||
|
ApplyRegistration(registration);
|
||||||
|
|
||||||
|
Mode = (DataBindingMode) Entity.DataBindingMode;
|
||||||
|
EasingTime = Entity.EasingTime;
|
||||||
|
EasingFunction = (Easings.Functions) Entity.EasingFunction;
|
||||||
|
|
||||||
|
// Data model is done during Initialize
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
foreach (var dataBindingModifierEntity in Entity.Modifiers)
|
||||||
|
_modifiers.Add(new DataBindingModifier<TLayerProperty, TProperty>(this, dataBindingModifierEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (!LayerProperty.Entity.DataBindingEntities.Contains(Entity))
|
||||||
|
LayerProperty.Entity.DataBindingEntities.Add(Entity);
|
||||||
|
|
||||||
|
// General
|
||||||
|
Entity.TargetProperty = TargetProperty?.Name;
|
||||||
|
Entity.DataBindingMode = (int) Mode;
|
||||||
|
Entity.EasingTime = EasingTime;
|
||||||
|
Entity.EasingFunction = (int) EasingFunction;
|
||||||
|
|
||||||
|
// Data model
|
||||||
|
Entity.SourceDataModelGuid = SourceDataModel?.PluginInfo?.Guid;
|
||||||
|
Entity.SourcePropertyPath = SourcePropertyPath;
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
Entity.Modifiers.Clear();
|
||||||
|
foreach (var dataBindingModifier in Modifiers)
|
||||||
|
dataBindingModifier.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,29 +1,29 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A data binding converter that acts as the bridge between a <see cref="DataBinding" /> and a
|
/// Represents a data binding converter that acts as the bridge between a
|
||||||
/// <see cref="LayerProperty{T}" />
|
/// <see cref="DataBinding{TLayerProperty, TProperty}" /> and a <see cref="LayerProperty{T}" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DataBindingConverter
|
public abstract class DataBindingConverter<TLayerProperty, TProperty> : IDataBindingConverter
|
||||||
{
|
{
|
||||||
internal Func<object> ValueGetter { get; set; }
|
/// <summary>
|
||||||
internal Action<object> ValueSetter { get; set; }
|
/// A dynamically compiled getter pointing to the data bound property
|
||||||
|
/// </summary>
|
||||||
|
public Func<TProperty> ValueGetter { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamically compiled setter pointing to the data bound property
|
||||||
|
/// </summary>
|
||||||
|
public Action<TProperty> ValueSetter { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data binding this converter is applied to
|
/// Gets the data binding this converter is applied to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBinding DataBinding { get; private set; }
|
public DataBinding<TLayerProperty, TProperty> DataBinding { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type this converter supports
|
|
||||||
/// </summary>
|
|
||||||
public Type SupportedType { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether or not this data binding converter supports the <see cref="Sum" /> method
|
/// Gets whether or not this data binding converter supports the <see cref="Sum" /> method
|
||||||
@ -35,17 +35,13 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsInterpolate { get; protected set; }
|
public bool SupportsInterpolate { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Called when the data binding converter has been initialized and the <see cref="DataBinding" /> is available
|
public Type SupportedType => typeof(TProperty);
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnInitialized()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the sum of <paramref name="a" /> and <paramref name="b" />
|
/// Returns the sum of <paramref name="a" /> and <paramref name="b" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract object Sum(object a, object b);
|
public abstract TProperty Sum(TProperty a, TProperty b);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the the interpolated value between <paramref name="a" /> and <paramref name="b" /> on a scale (generally)
|
/// Returns the the interpolated value between <paramref name="a" /> and <paramref name="b" /> on a scale (generally)
|
||||||
@ -56,20 +52,27 @@ namespace Artemis.Core
|
|||||||
/// <param name="b">The value to interpolate towards</param>
|
/// <param name="b">The value to interpolate towards</param>
|
||||||
/// <param name="progress">The progress of the interpolation between 0.0 and 1.0</param>
|
/// <param name="progress">The progress of the interpolation between 0.0 and 1.0</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract object Interpolate(object a, object b, double progress);
|
public abstract TProperty Interpolate(TProperty a, TProperty b, double progress);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies the <paramref name="value" /> to the layer property
|
/// Applies the <paramref name="value" /> to the layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public abstract void ApplyValue(object value);
|
public abstract void ApplyValue(TProperty value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the current base value of the data binding
|
/// Returns the current base value of the data binding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract object GetValue();
|
public abstract TProperty GetValue();
|
||||||
|
|
||||||
internal void Initialize(DataBinding dataBinding)
|
/// <summary>
|
||||||
|
/// Called when the data binding converter has been initialized and the <see cref="DataBinding" /> is available
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnInitialized()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Initialize(DataBinding<TLayerProperty, TProperty> dataBinding)
|
||||||
{
|
{
|
||||||
DataBinding = dataBinding;
|
DataBinding = dataBinding;
|
||||||
ValueGetter = CreateValueGetter();
|
ValueGetter = CreateValueGetter();
|
||||||
@ -78,7 +81,7 @@ namespace Artemis.Core
|
|||||||
OnInitialized();
|
OnInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Func<object> CreateValueGetter()
|
private Func<TProperty> CreateValueGetter()
|
||||||
{
|
{
|
||||||
if (DataBinding.TargetProperty?.DeclaringType == null)
|
if (DataBinding.TargetProperty?.DeclaringType == null)
|
||||||
return null;
|
return null;
|
||||||
@ -86,21 +89,19 @@ namespace Artemis.Core
|
|||||||
var getterMethod = DataBinding.TargetProperty.GetGetMethod();
|
var getterMethod = DataBinding.TargetProperty.GetGetMethod();
|
||||||
if (getterMethod == null)
|
if (getterMethod == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var constant = Expression.Constant(DataBinding.LayerProperty);
|
var constant = Expression.Constant(DataBinding.LayerProperty);
|
||||||
// The path is null if the registration is applied to the root (LayerProperty.CurrentValue)
|
// The path is null if the registration is applied to the root (LayerProperty.CurrentValue)
|
||||||
var property = DataBinding.Registration.Path == null
|
var property = DataBinding.Registration.Path == null
|
||||||
? Expression.Property(constant, DataBinding.TargetProperty)
|
? Expression.Property(constant, DataBinding.TargetProperty)
|
||||||
: (MemberExpression) DataBinding.Registration.Path.Split('.').Aggregate<string, Expression>(constant, Expression.Property);
|
: (MemberExpression) DataBinding.Registration.Path.Split('.').Aggregate<string, Expression>(constant, Expression.Property);
|
||||||
|
|
||||||
// The get method should cast to the object since it receives whatever type the property is
|
var lambda = Expression.Lambda<Func<TProperty>>(property);
|
||||||
var body = Expression.Convert(property, typeof(object));
|
|
||||||
var lambda = Expression.Lambda<Func<object>>(body);
|
|
||||||
|
|
||||||
return lambda.Compile();
|
return lambda.Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action<object> CreateValueSetter()
|
private Action<TProperty> CreateValueSetter()
|
||||||
{
|
{
|
||||||
if (DataBinding.TargetProperty?.DeclaringType == null)
|
if (DataBinding.TargetProperty?.DeclaringType == null)
|
||||||
return null;
|
return null;
|
||||||
@ -110,11 +111,10 @@ namespace Artemis.Core
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
var constant = Expression.Constant(DataBinding.LayerProperty);
|
var constant = Expression.Constant(DataBinding.LayerProperty);
|
||||||
var propertyValue = Expression.Parameter(typeof(object), "propertyValue");
|
var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
|
||||||
|
|
||||||
// The assign method should cast to the proper type since it receives an object
|
var body = Expression.Call(constant, setterMethod, propertyValue);
|
||||||
var body = Expression.Call(constant, setterMethod, Expression.Convert(propertyValue, DataBinding.TargetProperty.PropertyType));
|
var lambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
|
||||||
var lambda = Expression.Lambda<Action<object>>(body, propertyValue);
|
|
||||||
return lambda.Compile();
|
return lambda.Compile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,37 +8,33 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Modifies a data model value in a way defined by the modifier type
|
public class DataBindingModifier<TLayerProperty, TProperty> : IDataBindingModifier
|
||||||
/// </summary>
|
|
||||||
public class DataBindingModifier
|
|
||||||
{
|
{
|
||||||
private DataBinding _dataBinding;
|
private DataBinding<TLayerProperty, TProperty> _dataBinding;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the <see cref="DataBindingModifier" /> class
|
/// Creates a new instance of the <see cref="DataBindingModifier{TLayerProperty,TProperty}" /> class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parameterType">The type of the parameter, can either be dynamic (based on a data model value) or static</param>
|
/// <param name="parameterType">The type of the parameter, can either be dynamic (based on a data model value) or static</param>
|
||||||
public DataBindingModifier(ProfileRightSideType parameterType)
|
public DataBindingModifier(ProfileRightSideType parameterType)
|
||||||
{
|
{
|
||||||
ParameterType = parameterType;
|
ParameterType = parameterType;
|
||||||
Entity = new DataBindingModifierEntity();
|
Entity = new DataBindingModifierEntity();
|
||||||
|
Save();
|
||||||
ApplyToEntity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataBindingModifier(DataBinding dataBinding, DataBindingModifierEntity entity)
|
internal DataBindingModifier(DataBinding<TLayerProperty, TProperty> dataBinding, DataBindingModifierEntity entity)
|
||||||
{
|
{
|
||||||
DataBinding = dataBinding;
|
DataBinding = dataBinding;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
Load();
|
||||||
ApplyToDataBindingModifier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data binding this modifier is applied to
|
/// Gets the data binding this modifier is applied to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataBinding DataBinding
|
public DataBinding<TLayerProperty, TProperty> DataBinding
|
||||||
{
|
{
|
||||||
get => _dataBinding;
|
get => _dataBinding;
|
||||||
internal set
|
internal set
|
||||||
@ -218,7 +214,7 @@ namespace Artemis.Core
|
|||||||
// If deserialization fails, use the type's default
|
// If deserialization fails, use the type's default
|
||||||
catch (JsonSerializationException e)
|
catch (JsonSerializationException e)
|
||||||
{
|
{
|
||||||
dataBindingService.LogModifierDeserializationFailure(this, e);
|
dataBindingService.LogModifierDeserializationFailure(GetType().Name, e);
|
||||||
staticValue = Activator.CreateInstance(targetType);
|
staticValue = Activator.CreateInstance(targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,8 +222,12 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
{
|
{
|
||||||
|
if (!DataBinding.Entity.Modifiers.Contains(Entity))
|
||||||
|
DataBinding.Entity.Modifiers.Add(Entity);
|
||||||
|
|
||||||
// Modifier
|
// Modifier
|
||||||
Entity.ModifierType = ModifierType?.GetType().Name;
|
Entity.ModifierType = ModifierType?.GetType().Name;
|
||||||
Entity.ModifierTypePluginGuid = ModifierType?.PluginInfo.Guid;
|
Entity.ModifierTypePluginGuid = ModifierType?.PluginInfo.Guid;
|
||||||
@ -242,7 +242,8 @@ namespace Artemis.Core
|
|||||||
Entity.ParameterStaticValue = JsonConvert.SerializeObject(ParameterStaticValue);
|
Entity.ParameterStaticValue = JsonConvert.SerializeObject(ParameterStaticValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyToDataBindingModifier()
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
{
|
{
|
||||||
// Modifier type is done during Initialize
|
// Modifier type is done during Initialize
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,62 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public class DataBindingRegistration
|
/// <inheritdoc />
|
||||||
|
public class DataBindingRegistration<TLayerProperty, TProperty> : IDataBindingRegistration
|
||||||
{
|
{
|
||||||
internal DataBindingRegistration(BaseLayerProperty layerProperty, PropertyInfo property, DataBindingConverter converter, string path)
|
internal DataBindingRegistration(
|
||||||
|
LayerProperty<TLayerProperty> layerProperty,
|
||||||
|
DataBindingConverter<TLayerProperty, TProperty> converter,
|
||||||
|
PropertyInfo property,
|
||||||
|
string path)
|
||||||
{
|
{
|
||||||
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
|
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
|
||||||
Property = property ?? throw new ArgumentNullException(nameof(property));
|
|
||||||
Converter = converter ?? throw new ArgumentNullException(nameof(converter));
|
Converter = converter ?? throw new ArgumentNullException(nameof(converter));
|
||||||
Path = path;
|
Property = property ?? throw new ArgumentNullException(nameof(property));
|
||||||
|
Path = path ?? throw new ArgumentNullException(nameof(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataBinding DataBinding { get; internal set; }
|
/// <summary>
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
/// Gets the layer property this registration was made on
|
||||||
|
/// </summary>
|
||||||
|
public LayerProperty<TLayerProperty> LayerProperty { get; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the converter that's used by the data binding
|
||||||
|
/// </summary>
|
||||||
|
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the registered property
|
||||||
|
/// </summary>
|
||||||
public PropertyInfo Property { get; }
|
public PropertyInfo Property { get; }
|
||||||
public DataBindingConverter Converter { get; }
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the registered property on the layer property
|
||||||
|
/// </summary>
|
||||||
public string Path { get; }
|
public string Path { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the data binding created using this registration
|
||||||
|
/// </summary>
|
||||||
|
public DataBinding<TLayerProperty, TProperty> DataBinding { get; internal set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IDataBinding CreateDataBinding()
|
||||||
|
{
|
||||||
|
if (DataBinding != null)
|
||||||
|
return DataBinding;
|
||||||
|
|
||||||
|
var dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetProperty == Path);
|
||||||
|
if (dataBinding == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
DataBinding = new DataBinding<TLayerProperty, TProperty>(LayerProperty, dataBinding);
|
||||||
|
return DataBinding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
23
src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs
Normal file
23
src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data binding that binds a certain <see cref="LayerProperty{T}" /> to a value inside a <see cref="DataModel" />
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBinding : IStorageModel, IUpdateModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// (Re)initializes the data binding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataModelService"></param>
|
||||||
|
/// <param name="dataBindingService"></param>
|
||||||
|
void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the data binding to the layer property
|
||||||
|
/// </summary>
|
||||||
|
void Apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data binding converter that acts as the bridge between a
|
||||||
|
/// <see cref="DataBinding{TLayerProperty, TProperty}" /> and a <see cref="LayerProperty{T}" />
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBindingConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type this converter supports
|
||||||
|
/// </summary>
|
||||||
|
public Type SupportedType { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Modifies a data model value in a way defined by the modifier type
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBindingModifier : IStorageModel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data binding registration
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataBindingRegistration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// If found, creates a data binding from storage
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
IDataBinding CreateDataBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -34,8 +34,8 @@ namespace Artemis.Core
|
|||||||
Name = name;
|
Name = name;
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
DisplayContinuously = true;
|
DisplayContinuously = true;
|
||||||
General = new LayerGeneralProperties {IsCorePropertyGroup = true};
|
General = new LayerGeneralProperties();
|
||||||
Transform = new LayerTransformProperties {IsCorePropertyGroup = true};
|
Transform = new LayerTransformProperties();
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
@ -55,8 +55,8 @@ namespace Artemis.Core
|
|||||||
Name = layerEntity.Name;
|
Name = layerEntity.Name;
|
||||||
Enabled = layerEntity.Enabled;
|
Enabled = layerEntity.Enabled;
|
||||||
Order = layerEntity.Order;
|
Order = layerEntity.Order;
|
||||||
General = new LayerGeneralProperties {IsCorePropertyGroup = true};
|
General = new LayerGeneralProperties();
|
||||||
Transform = new LayerTransformProperties {IsCorePropertyGroup = true};
|
Transform = new LayerTransformProperties();
|
||||||
|
|
||||||
_layerEffects = new List<BaseLayerEffect>();
|
_layerEffects = new List<BaseLayerEffect>();
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
|
|||||||
@ -1,273 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// For internal use only, to implement your own layer property type, extend <see cref="LayerProperty{T}" /> instead.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class BaseLayerProperty
|
|
||||||
{
|
|
||||||
protected readonly List<DataBindingRegistration> _dataBindingRegistrations = new List<DataBindingRegistration>();
|
|
||||||
protected readonly List<DataBinding> _dataBindings = new List<DataBinding>();
|
|
||||||
|
|
||||||
private object _baseValue;
|
|
||||||
private object _currentValue;
|
|
||||||
private object _defaultValue;
|
|
||||||
private bool _isHidden;
|
|
||||||
private bool _keyframesEnabled;
|
|
||||||
|
|
||||||
internal BaseLayerProperty()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the base value of this layer property without any keyframes applied
|
|
||||||
/// </summary>
|
|
||||||
public object BaseValue
|
|
||||||
{
|
|
||||||
get => _baseValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != GetPropertyType())
|
|
||||||
throw new ArtemisCoreException("Cannot update base value because of a type mismatch");
|
|
||||||
_baseValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
|
||||||
/// </summary>
|
|
||||||
public object CurrentValue
|
|
||||||
{
|
|
||||||
get => _currentValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != GetPropertyType())
|
|
||||||
throw new ArtemisCoreException("Cannot update current value because of a type mismatch");
|
|
||||||
_currentValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property
|
|
||||||
/// has no value in storage
|
|
||||||
/// </summary>
|
|
||||||
public object DefaultValue
|
|
||||||
{
|
|
||||||
get => _defaultValue;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != GetPropertyType())
|
|
||||||
throw new ArtemisCoreException("Cannot update default value because of a type mismatch");
|
|
||||||
_defaultValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list containing the active data bindings
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<DataBinding> DataBindings => _dataBindings.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list containing all the data binding registrations
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<DataBindingRegistration> DataBindingRegistrations => _dataBindingRegistrations.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
|
||||||
/// </summary>
|
|
||||||
public RenderProfileElement ProfileElement { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parent group of this layer property, set after construction
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyGroup Parent { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether keyframes are supported on this type of property
|
|
||||||
/// </summary>
|
|
||||||
public bool KeyframesSupported { get; protected internal set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets whether data bindings are supported on this type of property
|
|
||||||
/// </summary>
|
|
||||||
public bool DataBindingsSupported { get; protected internal set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is
|
|
||||||
/// False
|
|
||||||
/// </summary>
|
|
||||||
public bool KeyframesEnabled
|
|
||||||
{
|
|
||||||
get => _keyframesEnabled;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_keyframesEnabled == value) return;
|
|
||||||
_keyframesEnabled = value;
|
|
||||||
OnKeyframesToggled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether the property is hidden in the UI
|
|
||||||
/// </summary>
|
|
||||||
public bool IsHidden
|
|
||||||
{
|
|
||||||
get => _isHidden;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isHidden = value;
|
|
||||||
OnVisibilityChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
|
||||||
/// </summary>
|
|
||||||
public bool IsLoadedFromStorage { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to declare that this property doesn't belong to a plugin and should use the core plugin GUID
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCoreProperty { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the description attribute applied to this property
|
|
||||||
/// </summary>
|
|
||||||
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of all the keyframes in their non-generic base form, without their values being available
|
|
||||||
/// </summary>
|
|
||||||
public abstract IReadOnlyList<BaseLayerPropertyKeyframe> BaseKeyframes { get; }
|
|
||||||
|
|
||||||
internal PropertyEntity PropertyEntity { get; set; }
|
|
||||||
internal LayerPropertyGroup LayerPropertyGroup { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overrides the property value with the default value
|
|
||||||
/// </summary>
|
|
||||||
public abstract void ApplyDefaultValue();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the type of the property
|
|
||||||
/// </summary>
|
|
||||||
public abstract Type GetPropertyType();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the provided property entity to the layer property by deserializing the JSON base value and keyframe values
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="entity"></param>
|
|
||||||
/// <param name="layerPropertyGroup"></param>
|
|
||||||
/// <param name="fromStorage"></param>
|
|
||||||
internal abstract void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves the property to the underlying property entity that was configured when calling
|
|
||||||
/// <see cref="ApplyToLayerProperty" />
|
|
||||||
/// </summary>
|
|
||||||
internal abstract void ApplyToEntity();
|
|
||||||
|
|
||||||
#region Data bindings
|
|
||||||
|
|
||||||
internal void InitializeDataBindings(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
|
||||||
{
|
|
||||||
foreach (var dataBinding in DataBindings)
|
|
||||||
dataBinding.Initialize(dataModelService, dataBindingService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a new data binding targeting the given property to the <see cref="DataBindings" /> collection
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The newly created data binding</returns>
|
|
||||||
public DataBinding EnableDataBinding(DataBindingRegistration dataBindingRegistration)
|
|
||||||
{
|
|
||||||
if (dataBindingRegistration.LayerProperty != this)
|
|
||||||
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
|
|
||||||
|
|
||||||
var dataBinding = new DataBinding(dataBindingRegistration);
|
|
||||||
_dataBindings.Add(dataBinding);
|
|
||||||
|
|
||||||
return dataBinding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the provided data binding from the <see cref="DataBindings" /> collection
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataBinding">The data binding to remove</param>
|
|
||||||
public void DisableDataBinding(DataBinding dataBinding)
|
|
||||||
{
|
|
||||||
_dataBindings.Remove(dataBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs once every frame when the layer property is updated
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> Updated;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when the base value of the layer property was updated
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> BaseValueChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> VisibilityChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when keyframes are enabled/disabled
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> KeyframesToggled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a new keyframe was added to the layer property
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> KeyframeAdded;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when a keyframe was removed from the layer property
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> KeyframeRemoved;
|
|
||||||
|
|
||||||
protected virtual void OnUpdated()
|
|
||||||
{
|
|
||||||
Updated?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnBaseValueChanged()
|
|
||||||
{
|
|
||||||
BaseValueChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnVisibilityChanged()
|
|
||||||
{
|
|
||||||
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnKeyframesToggled()
|
|
||||||
{
|
|
||||||
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnKeyframeAdded()
|
|
||||||
{
|
|
||||||
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnKeyframeRemoved()
|
|
||||||
{
|
|
||||||
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Stylet;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// For internal use only, use <see cref="LayerPropertyKeyframe{T}" /> instead.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class BaseLayerPropertyKeyframe : PropertyChangedBase
|
|
||||||
{
|
|
||||||
internal BaseLayerPropertyKeyframe(BaseLayerProperty baseLayerProperty)
|
|
||||||
{
|
|
||||||
BaseLayerProperty = baseLayerProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The base class of the layer property this keyframe is applied to
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerProperty BaseLayerProperty { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The position of this keyframe in the timeline
|
|
||||||
/// </summary>
|
|
||||||
public abstract TimeSpan Position { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The easing function applied on the value of the keyframe
|
|
||||||
/// </summary>
|
|
||||||
public Easings.Functions EasingFunction { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the keyframe from the layer property
|
|
||||||
/// </summary>
|
|
||||||
public abstract void Remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a property on a layer. Properties are saved in storage and can optionally be modified from the UI.
|
||||||
|
/// <para>
|
||||||
|
/// Note: You cannot initialize layer properties yourself. If properly placed and annotated, the Artemis core will
|
||||||
|
/// initialize these for you.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public interface ILayerProperty : IStorageModel, IUpdateModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the layer property
|
||||||
|
/// <para>
|
||||||
|
/// Note: This isn't done in the constructor to keep it parameterless which is easier for implementations of
|
||||||
|
/// <see cref="LayerProperty{T}" />
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Artemis.Core.Services;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@ -17,68 +17,114 @@ namespace Artemis.Core
|
|||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of property encapsulated in this layer property</typeparam>
|
/// <typeparam name="T">The type of property encapsulated in this layer property</typeparam>
|
||||||
public abstract class LayerProperty<T> : BaseLayerProperty
|
public abstract class LayerProperty<T> : ILayerProperty
|
||||||
{
|
{
|
||||||
private bool _isInitialized;
|
/// <summary>
|
||||||
private List<LayerPropertyKeyframe<T>> _keyframes;
|
/// Creates a new instance of the <see cref="LayerProperty{T}" /> class
|
||||||
|
/// </summary>
|
||||||
protected LayerProperty()
|
protected LayerProperty()
|
||||||
{
|
{
|
||||||
_keyframes = new List<LayerPropertyKeyframe<T>>();
|
_keyframes = new List<LayerPropertyKeyframe<T>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the base value of this layer property without any keyframes applied
|
/// Gets the description attribute applied to this property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new T BaseValue
|
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the property, applying keyframes and data bindings to the current value
|
||||||
|
/// </summary>
|
||||||
|
public void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
get => base.BaseValue != null ? (T) base.BaseValue : default;
|
CurrentValue = BaseValue;
|
||||||
|
|
||||||
|
UpdateKeyframes();
|
||||||
|
UpdateDataBindings(deltaTime);
|
||||||
|
|
||||||
|
OnUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the type of the property
|
||||||
|
/// </summary>
|
||||||
|
public Type GetPropertyType()
|
||||||
|
{
|
||||||
|
return typeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Hierarchy
|
||||||
|
|
||||||
|
private bool _isHidden;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the property is hidden in the UI
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHidden
|
||||||
|
{
|
||||||
|
get => _isHidden;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (Equals(base.BaseValue, value))
|
_isHidden = value;
|
||||||
|
OnVisibilityChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the profile element (such as layer or folder) this property is applied to
|
||||||
|
/// </summary>
|
||||||
|
public RenderProfileElement ProfileElement { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent group of this layer property, set after construction
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyGroup LayerPropertyGroup { get; internal set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Value management
|
||||||
|
|
||||||
|
private T _baseValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called every update (if keyframes are both supported and enabled) to determine the new <see cref="CurrentValue" />
|
||||||
|
/// based on the provided progress
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframeProgress">The linear current keyframe progress</param>
|
||||||
|
/// <param name="keyframeProgressEased">The current keyframe progress, eased with the current easing function</param>
|
||||||
|
protected virtual void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the base value of this layer property without any keyframes applied
|
||||||
|
/// </summary>
|
||||||
|
public T BaseValue
|
||||||
|
{
|
||||||
|
get => _baseValue;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Equals(_baseValue, value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
base.BaseValue = value;
|
_baseValue = value;
|
||||||
Update();
|
Update(0);
|
||||||
OnBaseValueChanged();
|
OnBaseValueChanged();
|
||||||
|
LayerPropertyGroup.OnLayerPropertyBaseValueChanged(new LayerPropertyEventArgs(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new T CurrentValue
|
public T CurrentValue { get; set; }
|
||||||
{
|
|
||||||
get => base.CurrentValue != null ? (T) base.CurrentValue : default;
|
|
||||||
set => base.CurrentValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property
|
/// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property
|
||||||
/// has no value in storage
|
/// has no value in storage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new T DefaultValue
|
public T DefaultValue { get; set; }
|
||||||
{
|
|
||||||
get => base.DefaultValue != null ? (T) base.DefaultValue : default;
|
|
||||||
set => base.DefaultValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a read-only list of all the keyframes on this layer property
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<LayerPropertyKeyframe<T>> Keyframes => _keyframes.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current keyframe in the timeline according to the current progress
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyKeyframe<T> CurrentKeyframe { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the next keyframe in the timeline according to the current progress
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyKeyframe<T> NextKeyframe { get; protected set; }
|
|
||||||
|
|
||||||
public override IReadOnlyList<BaseLayerPropertyKeyframe> BaseKeyframes => _keyframes.Cast<BaseLayerPropertyKeyframe>().ToList().AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the current value, using either keyframes if enabled or the base value.
|
/// Sets the current value, using either keyframes if enabled or the base value.
|
||||||
@ -105,9 +151,61 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
// Force an update so that the base value is applied to the current value and
|
// Force an update so that the base value is applied to the current value and
|
||||||
// keyframes/data bindings are applied using the new base value
|
// keyframes/data bindings are applied using the new base value
|
||||||
Update();
|
Update(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the property value with the default value
|
||||||
|
/// </summary>
|
||||||
|
public void ApplyDefaultValue()
|
||||||
|
{
|
||||||
|
BaseValue = DefaultValue;
|
||||||
|
CurrentValue = DefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Keyframes
|
||||||
|
|
||||||
|
private bool _keyframesEnabled;
|
||||||
|
private List<LayerPropertyKeyframe<T>> _keyframes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether keyframes are supported on this type of property
|
||||||
|
/// </summary>
|
||||||
|
public bool KeyframesSupported { get; protected internal set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is
|
||||||
|
/// False
|
||||||
|
/// </summary>
|
||||||
|
public bool KeyframesEnabled
|
||||||
|
{
|
||||||
|
get => _keyframesEnabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_keyframesEnabled == value) return;
|
||||||
|
_keyframesEnabled = value;
|
||||||
|
OnKeyframesToggled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only list of all the keyframes on this layer property
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<LayerPropertyKeyframe<T>> Keyframes => _keyframes.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current keyframe in the timeline according to the current progress
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyKeyframe<T> CurrentKeyframe { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the next keyframe in the timeline according to the current progress
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyKeyframe<T> NextKeyframe { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a keyframe to the layer property
|
/// Adds a keyframe to the layer property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -118,10 +216,9 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
keyframe.LayerProperty?.RemoveKeyframe(keyframe);
|
keyframe.LayerProperty?.RemoveKeyframe(keyframe);
|
||||||
|
|
||||||
keyframe.LayerProperty = this;
|
keyframe.LayerProperty = this;
|
||||||
keyframe.BaseLayerProperty = this;
|
|
||||||
_keyframes.Add(keyframe);
|
_keyframes.Add(keyframe);
|
||||||
|
|
||||||
SortKeyframes();
|
SortKeyframes();
|
||||||
OnKeyframeAdded();
|
OnKeyframeAdded();
|
||||||
}
|
}
|
||||||
@ -154,7 +251,6 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
_keyframes.Remove(keyframe);
|
_keyframes.Remove(keyframe);
|
||||||
keyframe.LayerProperty = null;
|
keyframe.LayerProperty = null;
|
||||||
keyframe.BaseLayerProperty = null;
|
|
||||||
SortKeyframes();
|
SortKeyframes();
|
||||||
OnKeyframeRemoved();
|
OnKeyframeRemoved();
|
||||||
}
|
}
|
||||||
@ -169,43 +265,6 @@ namespace Artemis.Core
|
|||||||
RemoveKeyframe(layerPropertyKeyframe);
|
RemoveKeyframe(layerPropertyKeyframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void ApplyDefaultValue()
|
|
||||||
{
|
|
||||||
BaseValue = DefaultValue;
|
|
||||||
CurrentValue = DefaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override Type GetPropertyType()
|
|
||||||
{
|
|
||||||
return typeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called every update (if keyframes are both supported and enabled) to determine the new <see cref="CurrentValue" />
|
|
||||||
/// based on the provided progress
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyframeProgress">The linear current keyframe progress</param>
|
|
||||||
/// <param name="keyframeProgressEased">The current keyframe progress, eased with the current easing function</param>
|
|
||||||
protected virtual void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the property, applying keyframes and data bindings to the current value
|
|
||||||
/// </summary>
|
|
||||||
internal void Update(double deltaTime = 0)
|
|
||||||
{
|
|
||||||
CurrentValue = BaseValue;
|
|
||||||
|
|
||||||
UpdateKeyframes();
|
|
||||||
UpdateDataBindings(deltaTime);
|
|
||||||
|
|
||||||
OnUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sorts the keyframes in ascending order by position
|
/// Sorts the keyframes in ascending order by position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -214,77 +273,6 @@ namespace Artemis.Core
|
|||||||
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage)
|
|
||||||
{
|
|
||||||
// Doubt this will happen but let's make sure
|
|
||||||
if (_isInitialized)
|
|
||||||
throw new ArtemisCoreException("Layer property already initialized, wut");
|
|
||||||
|
|
||||||
PropertyEntity = entity;
|
|
||||||
LayerPropertyGroup = layerPropertyGroup;
|
|
||||||
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (entity.Value != null)
|
|
||||||
BaseValue = JsonConvert.DeserializeObject<T>(entity.Value);
|
|
||||||
|
|
||||||
IsLoadedFromStorage = fromStorage;
|
|
||||||
CurrentValue = BaseValue;
|
|
||||||
KeyframesEnabled = entity.KeyframesEnabled;
|
|
||||||
|
|
||||||
_keyframes.Clear();
|
|
||||||
_keyframes.AddRange(entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe<T>(
|
|
||||||
JsonConvert.DeserializeObject<T>(k.Value),
|
|
||||||
k.Position,
|
|
||||||
(Easings.Functions) k.EasingFunction,
|
|
||||||
this
|
|
||||||
)));
|
|
||||||
|
|
||||||
_dataBindings.Clear();
|
|
||||||
foreach (var entityDataBindingEntity in entity.DataBindingEntities)
|
|
||||||
{
|
|
||||||
var dataBinding = new DataBinding(this, entityDataBindingEntity);
|
|
||||||
_dataBindings.Add(dataBinding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JsonException e)
|
|
||||||
{
|
|
||||||
// TODO: Properly log the JSON exception
|
|
||||||
Debug.WriteLine($"JSON exception while deserializing: {e}");
|
|
||||||
IsLoadedFromStorage = false;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
SortKeyframes();
|
|
||||||
_isInitialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
|
||||||
{
|
|
||||||
if (!_isInitialized)
|
|
||||||
throw new ArtemisCoreException("Layer property is not yet initialized");
|
|
||||||
|
|
||||||
PropertyEntity.Value = JsonConvert.SerializeObject(BaseValue);
|
|
||||||
PropertyEntity.KeyframesEnabled = KeyframesEnabled;
|
|
||||||
PropertyEntity.KeyframeEntities.Clear();
|
|
||||||
PropertyEntity.KeyframeEntities.AddRange(Keyframes.Select(k => new KeyframeEntity
|
|
||||||
{
|
|
||||||
Value = JsonConvert.SerializeObject(k.Value),
|
|
||||||
Position = k.Position,
|
|
||||||
EasingFunction = (int) k.EasingFunction
|
|
||||||
}));
|
|
||||||
|
|
||||||
PropertyEntity.DataBindingEntities.Clear();
|
|
||||||
foreach (var dataBinding in DataBindings)
|
|
||||||
{
|
|
||||||
dataBinding.ApplyToEntity();
|
|
||||||
PropertyEntity.DataBindingEntities.Add(dataBinding.Entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateKeyframes()
|
private void UpdateKeyframes()
|
||||||
{
|
{
|
||||||
if (!KeyframesSupported || !KeyframesEnabled)
|
if (!KeyframesSupported || !KeyframesEnabled)
|
||||||
@ -311,17 +299,32 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Data bindings
|
#region Data bindings
|
||||||
|
|
||||||
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda, DataBindingConverter converter)
|
internal readonly List<IDataBindingRegistration> _dataBindingRegistrations = new List<IDataBindingRegistration>();
|
||||||
|
internal readonly List<IDataBinding> _dataBindings = new List<IDataBinding>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether data bindings are supported on this type of property
|
||||||
|
/// </summary>
|
||||||
|
public bool DataBindingsSupported { get; protected internal set; } = true;
|
||||||
|
|
||||||
|
public DataBindingRegistration<T, TProperty> GetDataBindingRegistration<TProperty>(string propertyName)
|
||||||
|
{
|
||||||
|
var match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration<T, TProperty> registration &&
|
||||||
|
registration.Property.Name == propertyName);
|
||||||
|
return (DataBindingRegistration<T, TProperty>) match;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterDataBindingProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda, DataBindingConverter<T, TProperty> converter)
|
||||||
{
|
{
|
||||||
// If the lambda references to itself, use the property info of public new T CurrentValue
|
// If the lambda references to itself, use the property info of public new T CurrentValue
|
||||||
PropertyInfo propertyInfo;
|
PropertyInfo propertyInfo;
|
||||||
string path = null;
|
string path = null;
|
||||||
if (propertyLambda.Parameters[0] == propertyLambda.Body)
|
if (propertyLambda.Parameters[0] == propertyLambda.Body)
|
||||||
{
|
|
||||||
propertyInfo = GetType().GetProperties().FirstOrDefault(p => p.Name == nameof(CurrentValue) && p.PropertyType == typeof(T));
|
propertyInfo = GetType().GetProperties().FirstOrDefault(p => p.Name == nameof(CurrentValue) && p.PropertyType == typeof(T));
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
propertyInfo = ReflectionUtilities.GetPropertyInfo(CurrentValue, propertyLambda);
|
propertyInfo = ReflectionUtilities.GetPropertyInfo(CurrentValue, propertyLambda);
|
||||||
@ -341,18 +344,211 @@ namespace Artemis.Core
|
|||||||
"because the provided converter does not support the property's type");
|
"because the provided converter does not support the property's type");
|
||||||
}
|
}
|
||||||
|
|
||||||
_dataBindingRegistrations.Add(new DataBindingRegistration(this, propertyInfo, converter, path));
|
_dataBindingRegistrations.Add(new DataBindingRegistration<T, TProperty>(this, converter, propertyInfo, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables a data binding for the provided <paramref name="dataBindingRegistration" />
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The newly created data binding</returns>
|
||||||
|
public DataBinding<T, TProperty> EnableDataBinding<TProperty>(DataBindingRegistration<T, TProperty> dataBindingRegistration)
|
||||||
|
{
|
||||||
|
if (dataBindingRegistration.LayerProperty != this)
|
||||||
|
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
|
||||||
|
|
||||||
|
var dataBinding = new DataBinding<T, TProperty>(dataBindingRegistration);
|
||||||
|
_dataBindings.Add(dataBinding);
|
||||||
|
|
||||||
|
return dataBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the provided data binding
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataBinding">The data binding to remove</param>
|
||||||
|
public void DisableDataBinding<TProperty>(DataBinding<T, TProperty> dataBinding)
|
||||||
|
{
|
||||||
|
_dataBindings.Remove(dataBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateDataBindings(double deltaTime)
|
private void UpdateDataBindings(double deltaTime)
|
||||||
{
|
{
|
||||||
foreach (var dataBinding in DataBindings)
|
foreach (var dataBinding in _dataBindings)
|
||||||
{
|
{
|
||||||
dataBinding.Update(deltaTime);
|
dataBinding.Update(deltaTime);
|
||||||
dataBinding.ApplyToProperty();
|
dataBinding.Apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void InitializeDataBindings(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
||||||
|
{
|
||||||
|
foreach (var dataBinding in _dataBindings)
|
||||||
|
dataBinding.Initialize(dataModelService, dataBindingService);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Storage
|
||||||
|
|
||||||
|
private bool _isInitialized;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLoadedFromStorage { get; internal set; }
|
||||||
|
|
||||||
|
internal PropertyEntity Entity { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description)
|
||||||
|
{
|
||||||
|
_isInitialized = true;
|
||||||
|
|
||||||
|
ProfileElement = profileElement ?? throw new ArgumentNullException(nameof(profileElement));
|
||||||
|
LayerPropertyGroup = group ?? throw new ArgumentNullException(nameof(group));
|
||||||
|
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
||||||
|
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
||||||
|
IsLoadedFromStorage = fromStorage;
|
||||||
|
|
||||||
|
LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
if (!_isInitialized)
|
||||||
|
throw new ArtemisCoreException("Layer property is not yet initialized");
|
||||||
|
|
||||||
|
if (!IsLoadedFromStorage)
|
||||||
|
ApplyDefaultValue();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Entity.Value != null)
|
||||||
|
BaseValue = JsonConvert.DeserializeObject<T>(Entity.Value);
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
// ignored for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentValue = BaseValue;
|
||||||
|
KeyframesEnabled = Entity.KeyframesEnabled;
|
||||||
|
|
||||||
|
_keyframes.Clear();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_keyframes.AddRange(Entity.KeyframeEntities.Select(k => new LayerPropertyKeyframe<T>(
|
||||||
|
JsonConvert.DeserializeObject<T>(k.Value),
|
||||||
|
k.Position,
|
||||||
|
(Easings.Functions) k.EasingFunction,
|
||||||
|
this
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
// ignored for now
|
||||||
|
}
|
||||||
|
|
||||||
|
_dataBindings.Clear();
|
||||||
|
foreach (var dataBindingRegistration in _dataBindingRegistrations)
|
||||||
|
{
|
||||||
|
var dataBinding = dataBindingRegistration.CreateDataBinding();
|
||||||
|
if (dataBinding != null)
|
||||||
|
_dataBindings.Add(dataBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the property to the underlying property entity that was configured when calling
|
||||||
|
/// <see cref="ApplyToLayerProperty" />
|
||||||
|
/// </summary>
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (!_isInitialized)
|
||||||
|
throw new ArtemisCoreException("Layer property is not yet initialized");
|
||||||
|
|
||||||
|
Entity.Value = JsonConvert.SerializeObject(BaseValue);
|
||||||
|
Entity.KeyframesEnabled = KeyframesEnabled;
|
||||||
|
Entity.KeyframeEntities.Clear();
|
||||||
|
Entity.KeyframeEntities.AddRange(Keyframes.Select(k => new KeyframeEntity
|
||||||
|
{
|
||||||
|
Value = JsonConvert.SerializeObject(k.Value),
|
||||||
|
Position = k.Position,
|
||||||
|
EasingFunction = (int) k.EasingFunction
|
||||||
|
}));
|
||||||
|
|
||||||
|
Entity.DataBindingEntities.Clear();
|
||||||
|
foreach (var dataBinding in _dataBindings)
|
||||||
|
dataBinding.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs once every frame when the layer property is updated
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> Updated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the base value of the layer property was updated
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> BaseValueChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the <see cref="IsHidden" /> value of the layer property was updated
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> VisibilityChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when keyframes are enabled/disabled
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> KeyframesToggled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a new keyframe was added to the layer property
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> KeyframeAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when a keyframe was removed from the layer property
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<LayerPropertyEventArgs<T>> KeyframeRemoved;
|
||||||
|
|
||||||
|
protected virtual void OnUpdated()
|
||||||
|
{
|
||||||
|
Updated?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnBaseValueChanged()
|
||||||
|
{
|
||||||
|
BaseValueChanged?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnVisibilityChanged()
|
||||||
|
{
|
||||||
|
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframesToggled()
|
||||||
|
{
|
||||||
|
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframeAdded()
|
||||||
|
{
|
||||||
|
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframeRemoved()
|
||||||
|
{
|
||||||
|
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs<T>(this));
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,16 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public class LayerPropertyKeyframe<T> : BaseLayerPropertyKeyframe
|
/// <summary>
|
||||||
|
/// Represents a keyframe on a <see cref="LayerProperty{T}" /> containing a value and a timestamp
|
||||||
|
/// </summary>
|
||||||
|
public class LayerPropertyKeyframe<T> : PropertyChangedBase
|
||||||
{
|
{
|
||||||
private LayerProperty<T> _layerProperty;
|
private LayerProperty<T> _layerProperty;
|
||||||
private TimeSpan _position;
|
private TimeSpan _position;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction, LayerProperty<T> layerProperty) : base(layerProperty)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="LayerPropertyKeyframe{T}" /> class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value of the keyframe</param>
|
||||||
|
/// <param name="position">The position of this keyframe in the timeline</param>
|
||||||
|
/// <param name="easingFunction">The easing function applied on the value of the keyframe</param>
|
||||||
|
/// <param name="layerProperty">The layer property this keyframe is applied to</param>
|
||||||
|
public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction, LayerProperty<T> layerProperty)
|
||||||
{
|
{
|
||||||
_position = position;
|
_position = position;
|
||||||
|
|
||||||
Value = value;
|
Value = value;
|
||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
EasingFunction = easingFunction;
|
EasingFunction = easingFunction;
|
||||||
@ -34,8 +46,11 @@ namespace Artemis.Core
|
|||||||
set => SetAndNotify(ref _value, value);
|
set => SetAndNotify(ref _value, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override TimeSpan Position
|
/// <summary>
|
||||||
|
/// The position of this keyframe in the timeline
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan Position
|
||||||
{
|
{
|
||||||
get => _position;
|
get => _position;
|
||||||
set
|
set
|
||||||
@ -45,8 +60,15 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public override void Remove()
|
/// The easing function applied on the value of the keyframe
|
||||||
|
/// </summary>
|
||||||
|
public Easings.Functions EasingFunction { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the keyframe from the layer property
|
||||||
|
/// </summary>
|
||||||
|
public void Remove()
|
||||||
{
|
{
|
||||||
LayerProperty.RemoveKeyframe(this);
|
LayerProperty.RemoveKeyframe(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using Artemis.Storage.Entities.Profile;
|
namespace Artemis.Core
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
public class ColorGradientLayerProperty : LayerProperty<ColorGradient>
|
||||||
@ -9,10 +7,12 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
KeyframesSupported = false;
|
KeyframesSupported = false;
|
||||||
DataBindingsSupported = false;
|
DataBindingsSupported = false;
|
||||||
|
|
||||||
|
BaseValueChanged += OnBaseValueChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient"/>
|
/// Implicitly converts an <see cref="ColorGradientLayerProperty" /> to a <see cref="ColorGradient" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static implicit operator ColorGradient(ColorGradientLayerProperty p)
|
public static implicit operator ColorGradient(ColorGradientLayerProperty p)
|
||||||
{
|
{
|
||||||
@ -25,12 +25,11 @@ namespace Artemis.Core
|
|||||||
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
|
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToLayerProperty(PropertyEntity entity, LayerPropertyGroup layerPropertyGroup, bool fromStorage)
|
private void OnBaseValueChanged(object sender, LayerPropertyEventArgs<ColorGradient> e)
|
||||||
{
|
{
|
||||||
base.ApplyToLayerProperty(entity, layerPropertyGroup, fromStorage);
|
|
||||||
|
|
||||||
// Don't allow color gradients to be null
|
// Don't allow color gradients to be null
|
||||||
BaseValue ??= DefaultValue ?? new ColorGradient();
|
if (BaseValue == null)
|
||||||
|
BaseValue = DefaultValue ?? new ColorGradient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,11 +7,10 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal SKColorLayerProperty()
|
internal SKColorLayerProperty()
|
||||||
{
|
{
|
||||||
RegisterDataBindingProperty(color => color.Alpha, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Alpha));
|
RegisterDataBindingProperty(color => color.Alpha, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Alpha));
|
||||||
RegisterDataBindingProperty(color => color.Red, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Red));
|
RegisterDataBindingProperty(color => color.Red, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Red));
|
||||||
RegisterDataBindingProperty(color => color.Green, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Green));
|
RegisterDataBindingProperty(color => color.Green, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Green));
|
||||||
RegisterDataBindingProperty(color => color.Blue, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Blue));
|
RegisterDataBindingProperty(color => color.Blue, new SKColorArgbDataBindingConverter(SKColorArgbDataBindingConverter.Channel.Blue));
|
||||||
RegisterDataBindingProperty(color => color.Hue, new SKColorPartDataBindingConverter(SKColorPartDataBindingConverter.Channel.Hue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -7,8 +7,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal SKPointLayerProperty()
|
internal SKPointLayerProperty()
|
||||||
{
|
{
|
||||||
RegisterDataBindingProperty(point => point.X, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(point => point.X, new FloatDataBindingConverter<SKPoint>());
|
||||||
RegisterDataBindingProperty(point => point.Y, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(point => point.Y, new FloatDataBindingConverter<SKPoint>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -7,8 +7,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
internal SKSizeLayerProperty()
|
internal SKSizeLayerProperty()
|
||||||
{
|
{
|
||||||
RegisterDataBindingProperty(size => size.Width, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(size => size.Width, new FloatDataBindingConverter<SKSize>());
|
||||||
RegisterDataBindingProperty(size => size.Height, new FloatDataBindingConverter());
|
RegisterDataBindingProperty(size => size.Height, new FloatDataBindingConverter<SKSize>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -2,54 +2,53 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Artemis.Core.LayerBrushes;
|
using Artemis.Core.LayerBrushes;
|
||||||
using Artemis.Core.LayerEffects;
|
using Artemis.Core.LayerEffects;
|
||||||
using Artemis.Core.Properties;
|
using Artemis.Core.Properties;
|
||||||
using Artemis.Core.Services;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public abstract class LayerPropertyGroup : IDisposable
|
public abstract class LayerPropertyGroup : IDisposable
|
||||||
{
|
{
|
||||||
private readonly List<BaseLayerProperty> _layerProperties;
|
private readonly List<ILayerProperty> _layerProperties;
|
||||||
private readonly List<LayerPropertyGroup> _layerPropertyGroups;
|
private readonly List<LayerPropertyGroup> _layerPropertyGroups;
|
||||||
private ReadOnlyCollection<BaseLayerProperty> _allLayerProperties;
|
|
||||||
private bool _isHidden;
|
private bool _isHidden;
|
||||||
|
|
||||||
protected LayerPropertyGroup()
|
protected LayerPropertyGroup()
|
||||||
{
|
{
|
||||||
_layerProperties = new List<BaseLayerProperty>();
|
_layerProperties = new List<ILayerProperty>();
|
||||||
_layerPropertyGroups = new List<LayerPropertyGroup>();
|
_layerPropertyGroups = new List<LayerPropertyGroup>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
/// Gets the info of the plugin this group is associated with
|
||||||
|
/// </summary>
|
||||||
|
public PluginInfo PluginInfo { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the profile element (such as layer or folder) this group is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RenderProfileElement ProfileElement { get; internal set; }
|
public RenderProfileElement ProfileElement { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent group of this group
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyGroup Parent { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The path of this property group
|
/// The path of this property group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Path { get; internal set; }
|
public string Path { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parent group of this layer property group, set after construction
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyGroup Parent { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether this property groups properties are all initialized
|
/// Gets whether this property groups properties are all initialized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool PropertiesInitialized { get; private set; }
|
public bool PropertiesInitialized { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to declare that this property group doesn't belong to a plugin and should use the core plugin GUID
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCorePropertyGroup { get; internal set; }
|
|
||||||
|
|
||||||
public PropertyGroupDescriptionAttribute GroupDescription { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The layer brush this property group belongs to
|
/// The layer brush this property group belongs to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -76,13 +75,14 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of all layer properties in this group
|
/// A list of all layer properties in this group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<BaseLayerProperty> LayerProperties => _layerProperties.AsReadOnly();
|
public ReadOnlyCollection<ILayerProperty> LayerProperties => _layerProperties.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of al child groups in this group
|
/// A list of al child groups in this group
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyCollection<LayerPropertyGroup> LayerPropertyGroups => _layerPropertyGroups.AsReadOnly();
|
public ReadOnlyCollection<LayerPropertyGroup> LayerPropertyGroups => _layerPropertyGroups.AsReadOnly();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
DisableProperties();
|
DisableProperties();
|
||||||
@ -93,20 +93,16 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recursively gets all layer properties on this group and any subgroups
|
/// Recursively gets all layer properties on this group and any subgroups
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
public IReadOnlyCollection<ILayerProperty> GetAllLayerProperties()
|
||||||
public IReadOnlyCollection<BaseLayerProperty> GetAllLayerProperties()
|
|
||||||
{
|
{
|
||||||
if (!PropertiesInitialized)
|
if (!PropertiesInitialized)
|
||||||
return new List<BaseLayerProperty>();
|
return new List<ILayerProperty>();
|
||||||
if (_allLayerProperties != null)
|
|
||||||
return _allLayerProperties;
|
|
||||||
|
|
||||||
var result = new List<BaseLayerProperty>(LayerProperties);
|
var result = new List<ILayerProperty>(LayerProperties);
|
||||||
foreach (var layerPropertyGroup in LayerPropertyGroups)
|
foreach (var layerPropertyGroup in LayerPropertyGroups)
|
||||||
result.AddRange(layerPropertyGroup.GetAllLayerProperties());
|
result.AddRange(layerPropertyGroup.GetAllLayerProperties());
|
||||||
|
|
||||||
_allLayerProperties = result.AsReadOnly();
|
return result.AsReadOnly();
|
||||||
return _allLayerProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -116,7 +112,7 @@ namespace Artemis.Core
|
|||||||
protected abstract void PopulateDefaults();
|
protected abstract void PopulateDefaults();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the property group is deactivated
|
/// Called when the property group is aactivated
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract void EnableProperties();
|
protected abstract void EnableProperties();
|
||||||
|
|
||||||
@ -125,19 +121,26 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract void DisableProperties();
|
protected abstract void DisableProperties();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the property group and all its layer properties have been initialized
|
||||||
|
/// </summary>
|
||||||
protected virtual void OnPropertyGroupInitialized()
|
protected virtual void OnPropertyGroupInitialized()
|
||||||
{
|
{
|
||||||
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
|
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties(IRenderElementService renderElementService, RenderProfileElement profileElement, [NotNull] string path)
|
internal void Initialize(RenderProfileElement profileElement, [NotNull] string path, PluginInfo pluginInfo)
|
||||||
{
|
{
|
||||||
if (path == null)
|
if (path == null)
|
||||||
throw new ArgumentNullException(nameof(path));
|
throw new ArgumentNullException(nameof(path));
|
||||||
|
if (pluginInfo == null)
|
||||||
|
throw new ArgumentNullException(nameof(pluginInfo));
|
||||||
|
|
||||||
// Doubt this will happen but let's make sure
|
// Doubt this will happen but let's make sure
|
||||||
if (PropertiesInitialized)
|
if (PropertiesInitialized)
|
||||||
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
||||||
|
|
||||||
|
PluginInfo = pluginInfo;
|
||||||
ProfileElement = profileElement;
|
ProfileElement = profileElement;
|
||||||
Path = path.TrimEnd('.');
|
Path = path.TrimEnd('.');
|
||||||
|
|
||||||
@ -146,55 +149,21 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
||||||
if (propertyDescription != null)
|
if (propertyDescription != null)
|
||||||
{
|
InitializeProperty(propertyInfo, (PropertyDescriptionAttribute) propertyDescription);
|
||||||
if (!typeof(BaseLayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
|
||||||
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
var instance = (BaseLayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
|
|
||||||
if (instance == null)
|
|
||||||
throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
instance.ProfileElement = profileElement;
|
|
||||||
instance.Parent = this;
|
|
||||||
instance.PropertyDescription = (PropertyDescriptionAttribute) propertyDescription;
|
|
||||||
if (instance.PropertyDescription.DisableKeyframes)
|
|
||||||
instance.KeyframesSupported = false;
|
|
||||||
|
|
||||||
InitializeProperty(profileElement, path + propertyInfo.Name, instance);
|
|
||||||
|
|
||||||
propertyInfo.SetValue(this, instance);
|
|
||||||
_layerProperties.Add(instance);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
||||||
if (propertyGroupDescription != null)
|
if (propertyGroupDescription != null)
|
||||||
{
|
InitializeChildGroup(propertyInfo, (PropertyGroupDescriptionAttribute) propertyGroupDescription);
|
||||||
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
|
||||||
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
|
|
||||||
|
|
||||||
var instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType);
|
|
||||||
if (instance == null)
|
|
||||||
throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}");
|
|
||||||
|
|
||||||
instance.Parent = this;
|
|
||||||
instance.GroupDescription = (PropertyGroupDescriptionAttribute) propertyGroupDescription;
|
|
||||||
instance.LayerBrush = LayerBrush;
|
|
||||||
instance.LayerEffect = LayerEffect;
|
|
||||||
instance.InitializeProperties(renderElementService, profileElement, $"{path}{propertyInfo.Name}.");
|
|
||||||
|
|
||||||
propertyInfo.SetValue(this, instance);
|
|
||||||
_layerPropertyGroups.Add(instance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request the property group to populate defaults
|
// Request the property group to populate defaults
|
||||||
PopulateDefaults();
|
PopulateDefaults();
|
||||||
|
|
||||||
// Apply the newly populated defaults
|
// Load the layer properties after defaults have been applied
|
||||||
foreach (var layerProperty in _layerProperties.Where(p => !p.IsLoadedFromStorage))
|
foreach (var layerProperty in _layerProperties)
|
||||||
layerProperty.ApplyDefaultValue();
|
layerProperty.Load();
|
||||||
|
|
||||||
EnableProperties();
|
EnableProperties();
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
@ -206,25 +175,11 @@ namespace Artemis.Core
|
|||||||
if (!PropertiesInitialized)
|
if (!PropertiesInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get all properties with a PropertyDescriptionAttribute
|
foreach (var layerProperty in LayerProperties)
|
||||||
foreach (var propertyInfo in GetType().GetProperties())
|
layerProperty.Save();
|
||||||
{
|
|
||||||
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
foreach (var layerPropertyGroup in LayerPropertyGroups)
|
||||||
if (propertyDescription != null)
|
layerPropertyGroup.ApplyToEntity();
|
||||||
{
|
|
||||||
var layerProperty = (BaseLayerProperty) propertyInfo.GetValue(this);
|
|
||||||
layerProperty.ApplyToEntity();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
|
||||||
if (propertyGroupDescription != null)
|
|
||||||
{
|
|
||||||
var layerPropertyGroup = (LayerPropertyGroup) propertyInfo.GetValue(this);
|
|
||||||
layerPropertyGroup.ApplyToEntity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update(double deltaTime)
|
internal void Update(double deltaTime)
|
||||||
@ -234,34 +189,57 @@ namespace Artemis.Core
|
|||||||
OnPropertyGroupUpdating(new LayerPropertyGroupUpdatingEventArgs(deltaTime));
|
OnPropertyGroupUpdating(new LayerPropertyGroupUpdatingEventArgs(deltaTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeProperty(RenderProfileElement profileElement, string path, BaseLayerProperty instance)
|
private void InitializeProperty(PropertyInfo propertyInfo, PropertyDescriptionAttribute propertyDescription)
|
||||||
{
|
{
|
||||||
Guid pluginGuid;
|
var path = Path + ".";
|
||||||
if (IsCorePropertyGroup || instance.IsCoreProperty)
|
|
||||||
pluginGuid = Constants.CorePluginInfo.Guid;
|
|
||||||
else if (instance.Parent.LayerBrush != null)
|
|
||||||
pluginGuid = instance.Parent.LayerBrush.PluginInfo.Guid;
|
|
||||||
else
|
|
||||||
pluginGuid = instance.Parent.LayerEffect.PluginInfo.Guid;
|
|
||||||
|
|
||||||
var entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == pluginGuid && p.Path == path);
|
if (!typeof(ILayerProperty).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
var fromStorage = true;
|
throw new ArtemisPluginException($"Layer property with PropertyDescription attribute must be of type LayerProperty at {path + propertyInfo.Name}");
|
||||||
|
|
||||||
|
var instance = (ILayerProperty) Activator.CreateInstance(propertyInfo.PropertyType, true);
|
||||||
|
if (instance == null)
|
||||||
|
throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
|
||||||
|
|
||||||
|
var entity = GetPropertyEntity(ProfileElement, path + propertyInfo.Name, out var fromStorage);
|
||||||
|
instance.Initialize(ProfileElement, this, entity, fromStorage, propertyDescription);
|
||||||
|
propertyInfo.SetValue(this, instance);
|
||||||
|
_layerProperties.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeChildGroup(PropertyInfo propertyInfo, PropertyGroupDescriptionAttribute propertyGroupDescription)
|
||||||
|
{
|
||||||
|
var path = Path + ".";
|
||||||
|
|
||||||
|
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
|
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
|
||||||
|
|
||||||
|
var instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType);
|
||||||
|
if (instance == null)
|
||||||
|
throw new ArtemisPluginException($"Failed to create instance of layer property group at {path + propertyInfo.Name}");
|
||||||
|
|
||||||
|
instance.Parent = this;
|
||||||
|
instance.GroupDescription = propertyGroupDescription;
|
||||||
|
instance.LayerBrush = LayerBrush;
|
||||||
|
instance.LayerEffect = LayerEffect;
|
||||||
|
instance.Initialize(ProfileElement, $"{path}{propertyInfo.Name}.", PluginInfo);
|
||||||
|
|
||||||
|
propertyInfo.SetValue(this, instance);
|
||||||
|
_layerPropertyGroups.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropertyEntity GetPropertyEntity(RenderProfileElement profileElement, string path, out bool fromStorage)
|
||||||
|
{
|
||||||
|
var entity = profileElement.RenderElementEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == PluginInfo.Guid && p.Path == path);
|
||||||
|
fromStorage = entity != null;
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
{
|
{
|
||||||
fromStorage = false;
|
entity = new PropertyEntity {PluginGuid = PluginInfo.Guid, Path = path};
|
||||||
entity = new PropertyEntity {PluginGuid = pluginGuid, Path = path};
|
|
||||||
profileElement.RenderElementEntity.PropertyEntities.Add(entity);
|
profileElement.RenderElementEntity.PropertyEntities.Add(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.ApplyToLayerProperty(entity, this, fromStorage);
|
return entity;
|
||||||
instance.BaseValueChanged += InstanceOnBaseValueChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InstanceOnBaseValueChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
OnLayerPropertyBaseValueChanged(new LayerPropertyEventArgs((BaseLayerProperty) sender));
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
internal event EventHandler<LayerPropertyGroupUpdatingEventArgs> PropertyGroupUpdating;
|
internal event EventHandler<LayerPropertyGroupUpdatingEventArgs> PropertyGroupUpdating;
|
||||||
@ -282,17 +260,17 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler VisibilityChanged;
|
public event EventHandler VisibilityChanged;
|
||||||
|
|
||||||
protected virtual void OnPropertyGroupUpdating(LayerPropertyGroupUpdatingEventArgs e)
|
internal virtual void OnPropertyGroupUpdating(LayerPropertyGroupUpdatingEventArgs e)
|
||||||
{
|
{
|
||||||
PropertyGroupUpdating?.Invoke(this, e);
|
PropertyGroupUpdating?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnVisibilityChanged()
|
internal virtual void OnVisibilityChanged()
|
||||||
{
|
{
|
||||||
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void OnLayerPropertyBaseValueChanged(LayerPropertyEventArgs e)
|
internal virtual void OnLayerPropertyBaseValueChanged(LayerPropertyEventArgs e)
|
||||||
{
|
{
|
||||||
LayerPropertyBaseValueChanged?.Invoke(this, e);
|
LayerPropertyBaseValueChanged?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Core.Services;
|
|
||||||
|
|
||||||
namespace Artemis.Core.LayerBrushes
|
namespace Artemis.Core.LayerBrushes
|
||||||
{
|
{
|
||||||
@ -33,11 +32,11 @@ namespace Artemis.Core.LayerBrushes
|
|||||||
internal set => _properties = value;
|
internal set => _properties = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties(IRenderElementService renderElementService)
|
internal void InitializeProperties()
|
||||||
{
|
{
|
||||||
Properties = Activator.CreateInstance<T>();
|
Properties = Activator.CreateInstance<T>();
|
||||||
Properties.LayerBrush = this;
|
Properties.LayerBrush = this;
|
||||||
Properties.InitializeProperties(renderElementService, Layer, "LayerBrush.");
|
Properties.Initialize(Layer, "LayerBrush.", PluginInfo);
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
EnableLayerBrush();
|
EnableLayerBrush();
|
||||||
|
|||||||
@ -30,11 +30,11 @@ namespace Artemis.Core.LayerEffects
|
|||||||
internal set => _properties = value;
|
internal set => _properties = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void InitializeProperties(IRenderElementService renderElementService)
|
internal void InitializeProperties()
|
||||||
{
|
{
|
||||||
Properties = Activator.CreateInstance<T>();
|
Properties = Activator.CreateInstance<T>();
|
||||||
Properties.LayerEffect = this;
|
Properties.LayerEffect = this;
|
||||||
Properties.InitializeProperties(renderElementService, ProfileElement, PropertyRootPath);
|
Properties.Initialize(ProfileElement, PropertyRootPath, PluginInfo);
|
||||||
PropertiesInitialized = true;
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
EnableLayerEffect();
|
EnableLayerEffect();
|
||||||
|
|||||||
@ -91,14 +91,9 @@ namespace Artemis.Core.Services
|
|||||||
return RegisteredDataBindingModifierTypes.FirstOrDefault(o => o.PluginInfo.Guid == modifierTypePluginGuid && o.GetType().Name == modifierType);
|
return RegisteredDataBindingModifierTypes.FirstOrDefault(o => o.PluginInfo.Guid == modifierTypePluginGuid && o.GetType().Name == modifierType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception)
|
public void LogModifierDeserializationFailure(string modifierName, JsonSerializationException exception)
|
||||||
{
|
{
|
||||||
_logger.Warning(
|
_logger.Warning(exception, "Failed to deserialize static parameter for modifier {modifierName}", modifierName);
|
||||||
exception,
|
|
||||||
"Failed to deserialize static parameter for operator {order}. {operatorType}",
|
|
||||||
dataBindingModifier.Entity.Order,
|
|
||||||
dataBindingModifier.Entity.ModifierType
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterBuiltInModifiers()
|
private void RegisterBuiltInModifiers()
|
||||||
|
|||||||
@ -41,8 +41,8 @@ namespace Artemis.Core.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a modifier deserialization failure
|
/// Logs a modifier deserialization failure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataBindingModifier">The modifier that failed to deserialize</param>
|
/// <param name="modifierName">The modifier that failed to deserialize</param>
|
||||||
/// <param name="exception">The JSON exception that occurred</param>
|
/// <param name="exception">The JSON exception that occurred</param>
|
||||||
void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception);
|
void LogModifierDeserializationFailure(string modifierName, JsonSerializationException exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,8 +30,8 @@ namespace Artemis.Core.Services
|
|||||||
parent.AddChild(layer);
|
parent.AddChild(layer);
|
||||||
|
|
||||||
// Layers have two hardcoded property groups, instantiate them
|
// Layers have two hardcoded property groups, instantiate them
|
||||||
layer.General.InitializeProperties(this, layer, "General.");
|
layer.General.Initialize(layer, "General.", Constants.CorePluginInfo);
|
||||||
layer.Transform.InitializeProperties(this, layer, "Transform.");
|
layer.Transform.Initialize(layer, "Transform.", Constants.CorePluginInfo);
|
||||||
|
|
||||||
// With the properties loaded, the layer brush and effect can be instantiated
|
// With the properties loaded, the layer brush and effect can be instantiated
|
||||||
InstantiateLayerBrush(layer);
|
InstantiateLayerBrush(layer);
|
||||||
|
|||||||
@ -278,9 +278,9 @@ namespace Artemis.Core.Services
|
|||||||
foreach (var layer in profile.GetAllLayers())
|
foreach (var layer in profile.GetAllLayers())
|
||||||
{
|
{
|
||||||
if (!layer.General.PropertiesInitialized)
|
if (!layer.General.PropertiesInitialized)
|
||||||
layer.General.InitializeProperties(_renderElementService, layer, "General.");
|
layer.General.Initialize(layer, "General.", Constants.CorePluginInfo);
|
||||||
if (!layer.Transform.PropertiesInitialized)
|
if (!layer.Transform.PropertiesInitialized)
|
||||||
layer.Transform.InitializeProperties(_renderElementService, layer, "Transform.");
|
layer.Transform.Initialize(layer, "Transform.", Constants.CorePluginInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user