Core - Layer refactor WIP

This commit is contained in:
Robert authored and SpoinkyNL committed 2020-09-08 19:17:04 +02:00
1 parent be897b99f7
commit d37420e462
33 files changed
+941 -895

No files matched your search

@@ -3,46 +3,51 @@
namespace Artemis.Core
{
/// <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()
{
SupportedType = typeof(float);
SupportsSum = true;
SupportsInterpolate = true;
}
/// <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 />
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 floatB = Convert.ToSingle(b);
var diff = floatB - floatA;
return floatA + diff * progress;
var diff = a - b;
return (float) (a + diff * progress);
}
/// <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)
floatValue = Math.Min(floatValue, max);
value = Math.Min(value, max);
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 />
public override object GetValue()
public override float GetValue()
{
return ValueGetter?.Invoke();
return ValueGetter?.Invoke() ?? 0f;
}
}
}
@@ -3,11 +3,10 @@
namespace Artemis.Core
{
/// <inheritdoc />
public class GeneralDataBindingConverter : DataBindingConverter
public class GeneralDataBindingConverter<T> : DataBindingConverter<T, object> where T : ILayerProperty
{
public GeneralDataBindingConverter()
{
SupportedType = typeof(object);
SupportsSum = false;
SupportsInterpolate = false;
}
@@ -3,11 +3,18 @@
namespace Artemis.Core
{
/// <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()
{
SupportedType = typeof(int);
SupportsSum = true;
SupportsInterpolate = true;
}
@@ -19,30 +26,28 @@ namespace Artemis.Core
public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero;
/// <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 />
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 intB = (int) b;
var diff = intB - intA;
return (int) Math.Round(intA + diff * progress, InterpolationRoundingMode);
var diff = b - a;
return (int) Math.Round(a + diff * progress, InterpolationRoundingMode);
}
/// <inheritdoc />
public override void ApplyValue(object value)
public override void ApplyValue(int value)
{
ValueSetter?.Invoke(value);
}
/// <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
{
/// <summary>
/// A data binding that binds a certain <see cref="BaseLayerProperty" /> to a value inside a <see cref="DataModel" />
/// </summary>
public class DataBinding
/// <inheritdoc />
public class DataBinding<TLayerProperty, TProperty> : IDataBinding
{
private readonly List<DataBindingModifier> _modifiers = new List<DataBindingModifier>();
private readonly List<DataBindingModifier<TLayerProperty, TProperty>> _modifiers = new List<DataBindingModifier<TLayerProperty, TProperty>>();
private object _currentValue;
private object _previousValue;
private TProperty _currentValue;
private TimeSpan _easingProgress;
private TProperty _previousValue;
internal DataBinding(DataBindingRegistration dataBindingRegistration)
internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
{
LayerProperty = dataBindingRegistration.LayerProperty;
Entity = new DataBindingEntity();
ApplyRegistration(dataBindingRegistration);
ApplyToEntity();
Save();
}
internal DataBinding(BaseLayerProperty layerProperty, DataBindingEntity entity)
internal DataBinding(LayerProperty<TLayerProperty> layerProperty, DataBindingEntity entity)
{
LayerProperty = layerProperty;
Entity = entity;
ApplyToDataBinding();
Load();
}
/// <summary>
/// Gets the layer property this data binding targets
/// </summary>
public BaseLayerProperty LayerProperty { get; }
/// <summary>
/// Gets the data binding registration this data binding is based upon
/// </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>
/// Gets the converter used to apply this data binding to the <see cref="LayerProperty" />
/// </summary>
public DataBindingConverter Converter { get; private set; }
public DataBindingConverter<TLayerProperty, TProperty> Converter { get; private set; }
/// <summary>
/// Gets the property on the <see cref="LayerProperty" /> this data binding targets
@@ -82,7 +80,7 @@ namespace Artemis.Core
/// <summary>
/// Gets a list of modifiers applied to this data binding
/// </summary>
public IReadOnlyList<DataBindingModifier> Modifiers => _modifiers.AsReadOnly();
public IReadOnlyList<DataBindingModifier<TLayerProperty, TProperty>> Modifiers => _modifiers.AsReadOnly();
/// <summary>
/// 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; }
/// <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>
/// Adds a modifier to the data binding's <see cref="Modifiers" /> collection
/// </summary>
public void AddModifier(DataBindingModifier modifier)
public void AddModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
{
if (!_modifiers.Contains(modifier))
{
@@ -108,7 +120,7 @@ namespace Artemis.Core
/// <summary>
/// Removes a modifier from the data binding's <see cref="Modifiers" /> collection
/// </summary>
public void RemoveModifier(DataBindingModifier modifier)
public void RemoveModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
{
if (_modifiers.Contains(modifier))
{
@@ -147,7 +159,7 @@ namespace Artemis.Core
/// </summary>
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
/// <returns></returns>
public object GetValue(object baseValue)
public TProperty GetValue(TProperty baseValue)
{
if (CompiledTargetAccessor == null || Converter == null)
return baseValue;
@@ -156,7 +168,7 @@ namespace Artemis.Core
foreach (var dataBindingModifier in Modifiers)
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 (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
@@ -170,13 +182,6 @@ namespace Artemis.Core
return GetInterpolatedValue();
}
private void ResetEasing(object value)
{
_previousValue = GetInterpolatedValue();
_currentValue = value;
_easingProgress = TimeSpan.Zero;
}
/// <summary>
/// Returns the type of the target property of this data binding
/// </summary>
@@ -193,24 +198,8 @@ namespace Artemis.Core
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
}
/// <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>
/// Updates the value on the <see cref="LayerProperty" /> according to the data binding
/// </summary>
public void ApplyToProperty()
/// <inheritdoc />
public void Apply()
{
if (Converter == null)
return;
@@ -219,44 +208,8 @@ namespace Artemis.Core
Converter.ApplyValue(GetValue(value));
}
internal void ApplyToEntity()
{
// 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)
/// <inheritdoc />
public void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
{
// Source
if (Entity.SourceDataModelGuid != null && SourceDataModel == null)
@@ -271,7 +224,14 @@ namespace Artemis.Core
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)
dataBindingRegistration.DataBinding = this;
@@ -283,15 +243,15 @@ namespace Artemis.Core
if (GetTargetType().IsValueType)
{
if (_currentValue == null)
_currentValue = GetTargetType().GetDefault();
_currentValue = default;
if (_previousValue == null)
_previousValue = _currentValue;
_previousValue = default;
}
Converter?.Initialize(this);
}
private object GetInterpolatedValue()
private TProperty GetInterpolatedValue()
{
if (_easingProgress == EasingTime || !Converter.SupportsInterpolate)
return _currentValue;
@@ -318,6 +278,50 @@ namespace Artemis.Core
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
/// <summary>
@@ -1,29 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Artemis.Core
{
/// <summary>
/// A data binding converter that acts as the bridge between a <see cref="DataBinding" /> and a
/// <see cref="LayerProperty{T}" />
/// 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 abstract class DataBindingConverter
public abstract class DataBindingConverter<TLayerProperty, TProperty> : IDataBindingConverter
{
internal Func<object> ValueGetter { get; set; }
internal Action<object> ValueSetter { get; set; }
/// <summary>
/// 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>
/// Gets the data binding this converter is applied to
/// </summary>
public DataBinding DataBinding { get; private set; }
/// <summary>
/// Gets the type this converter supports
/// </summary>
public Type SupportedType { get; protected set; }
public DataBinding<TLayerProperty, TProperty> DataBinding { get; private set; }
/// <summary>
/// Gets whether or not this data binding converter supports the <see cref="Sum" /> method
@@ -35,17 +35,13 @@ namespace Artemis.Core
/// </summary>
public bool SupportsInterpolate { get; protected set; }
/// <summary>
/// Called when the data binding converter has been initialized and the <see cref="DataBinding" /> is available
/// </summary>
protected virtual void OnInitialized()
{
}
/// <inheritdoc />
public Type SupportedType => typeof(TProperty);
/// <summary>
/// Returns the sum of <paramref name="a" /> and <paramref name="b" />
/// </summary>
public abstract object Sum(object a, object b);
public abstract TProperty Sum(TProperty a, TProperty b);
/// <summary>
/// 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="progress">The progress of the interpolation between 0.0 and 1.0</param>
/// <returns></returns>
public abstract object Interpolate(object a, object b, double progress);
public abstract TProperty Interpolate(TProperty a, TProperty b, double progress);
/// <summary>
/// Applies the <paramref name="value" /> to the layer property
/// </summary>
/// <param name="value"></param>
public abstract void ApplyValue(object value);
public abstract void ApplyValue(TProperty value);
/// <summary>
/// Returns the current base value of the data binding
/// </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;
ValueGetter = CreateValueGetter();
@@ -78,7 +81,7 @@ namespace Artemis.Core
OnInitialized();
}
private Func<object> CreateValueGetter()
private Func<TProperty> CreateValueGetter()
{
if (DataBinding.TargetProperty?.DeclaringType == null)
return null;
@@ -86,21 +89,19 @@ namespace Artemis.Core
var getterMethod = DataBinding.TargetProperty.GetGetMethod();
if (getterMethod == null)
return null;
var constant = Expression.Constant(DataBinding.LayerProperty);
// The path is null if the registration is applied to the root (LayerProperty.CurrentValue)
var property = DataBinding.Registration.Path == null
? Expression.Property(constant, DataBinding.TargetProperty)
var property = DataBinding.Registration.Path == null
? Expression.Property(constant, DataBinding.TargetProperty)
: (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 body = Expression.Convert(property, typeof(object));
var lambda = Expression.Lambda<Func<object>>(body);
var lambda = Expression.Lambda<Func<TProperty>>(property);
return lambda.Compile();
}
private Action<object> CreateValueSetter()
private Action<TProperty> CreateValueSetter()
{
if (DataBinding.TargetProperty?.DeclaringType == null)
return null;
@@ -110,11 +111,10 @@ namespace Artemis.Core
return null;
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, Expression.Convert(propertyValue, DataBinding.TargetProperty.PropertyType));
var lambda = Expression.Lambda<Action<object>>(body, propertyValue);
var body = Expression.Call(constant, setterMethod, propertyValue);
var lambda = Expression.Lambda<Action<TProperty>>(body, propertyValue);
return lambda.Compile();
}
}
@@ -8,37 +8,33 @@ using Newtonsoft.Json;
namespace Artemis.Core
{
/// <summary>
/// Modifies a data model value in a way defined by the modifier type
/// </summary>
public class DataBindingModifier
/// <inheritdoc />
public class DataBindingModifier<TLayerProperty, TProperty> : IDataBindingModifier
{
private DataBinding _dataBinding;
private DataBinding<TLayerProperty, TProperty> _dataBinding;
/// <summary>
/// Creates a new instance of the <see cref="DataBindingModifier" /> class
/// Creates a new instance of the <see cref="DataBindingModifier{TLayerProperty,TProperty}" /> class
/// </summary>
/// <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)
{
ParameterType = parameterType;
Entity = new DataBindingModifierEntity();
ApplyToEntity();
Save();
}
internal DataBindingModifier(DataBinding dataBinding, DataBindingModifierEntity entity)
internal DataBindingModifier(DataBinding<TLayerProperty, TProperty> dataBinding, DataBindingModifierEntity entity)
{
DataBinding = dataBinding;
Entity = entity;
ApplyToDataBindingModifier();
Load();
}
/// <summary>
/// Gets the data binding this modifier is applied to
/// </summary>
public DataBinding DataBinding
public DataBinding<TLayerProperty, TProperty> DataBinding
{
get => _dataBinding;
internal set
@@ -218,7 +214,7 @@ namespace Artemis.Core
// If deserialization fails, use the type's default
catch (JsonSerializationException e)
{
dataBindingService.LogModifierDeserializationFailure(this, e);
dataBindingService.LogModifierDeserializationFailure(GetType().Name, e);
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
Entity.ModifierType = ModifierType?.GetType().Name;
Entity.ModifierTypePluginGuid = ModifierType?.PluginInfo.Guid;
@@ -242,7 +242,8 @@ namespace Artemis.Core
Entity.ParameterStaticValue = JsonConvert.SerializeObject(ParameterStaticValue);
}
internal void ApplyToDataBindingModifier()
/// <inheritdoc />
public void Load()
{
// Modifier type is done during Initialize
@@ -1,22 +1,62 @@
using System;
using System.Linq;
using System.Reflection;
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));
Property = property ?? throw new ArgumentNullException(nameof(property));
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; }
public BaseLayerProperty LayerProperty { get; }
/// <summary>
/// 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 DataBindingConverter Converter { get; }
/// <summary>
/// Gets the path of the registered property on the layer property
/// </summary>
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;
}
}
}
@@ -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();
}
}