mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Core - Layer refactor WIP
This commit is contained in:
1 parent
be897b99f7
commit
d37420e462
33 files changed
+941
-895
No files matched your search
+21
-16
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -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;
|
||||
}
|
||||
|
||||
+17
-12
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
-95
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user