mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Data bindings - Rewrote data binding system to fully leverage nodes
This commit is contained in:
parent
7933bf7ee9
commit
0bb93f2ebd
@ -84,4 +84,8 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="DefaultTypes\DataBindings\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -1,47 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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()
|
||||
{
|
||||
SupportsSum = true;
|
||||
SupportsInterpolate = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override float Sum(float a, float b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override float Interpolate(float a, float b, double progress)
|
||||
{
|
||||
float diff = b - a;
|
||||
return (float) (a + diff * progress);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ApplyValue(float value)
|
||||
{
|
||||
if (DataBinding!.LayerProperty.PropertyDescription.MaxInputValue is float max)
|
||||
value = Math.Min(value, max);
|
||||
if (DataBinding!.LayerProperty.PropertyDescription.MinInputValue is float min)
|
||||
value = Math.Max(value, min);
|
||||
|
||||
base.ApplyValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic data binding converter that acts as the bridge between a
|
||||
/// <see cref="DataBinding{TLayerProperty, TProperty}" /> and a <see cref="LayerProperty{T}" /> and does not support
|
||||
/// sum or interpolation
|
||||
/// </summary>
|
||||
public class GeneralDataBindingConverter<T> : DataBindingConverter<T, T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="GeneralDataBindingConverter{T}" /> class
|
||||
/// </summary>
|
||||
public GeneralDataBindingConverter()
|
||||
{
|
||||
SupportsSum = false;
|
||||
SupportsInterpolate = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override T Sum(T a, T b)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override T Interpolate(T a, T b, double progress)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class IntDataBindingConverter : IntDataBindingConverter<int>
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public class IntDataBindingConverter<T> : DataBindingConverter<T, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="IntDataBindingConverter{T}" /> class
|
||||
/// </summary>
|
||||
public IntDataBindingConverter()
|
||||
{
|
||||
SupportsSum = true;
|
||||
SupportsInterpolate = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="MidpointRounding" /> mode used for rounding during interpolation. Defaults to
|
||||
/// <see cref="MidpointRounding.AwayFromZero" />
|
||||
/// </summary>
|
||||
public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Sum(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Interpolate(int a, int b, double progress)
|
||||
{
|
||||
int diff = b - a;
|
||||
return (int) Math.Round(a + diff * progress, InterpolationRoundingMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class SKColorDataBindingConverter : DataBindingConverter<SKColor, SKColor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SKColorDataBindingConverter" /> class
|
||||
/// </summary>
|
||||
public SKColorDataBindingConverter()
|
||||
{
|
||||
SupportsInterpolate = true;
|
||||
SupportsSum = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SKColor Sum(SKColor a, SKColor b)
|
||||
{
|
||||
return a.Sum(b);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SKColor Interpolate(SKColor a, SKColor b, double progress)
|
||||
{
|
||||
return a.Interpolate(b, (float) progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,14 @@
|
||||
public class BoolLayerProperty : LayerProperty<bool>
|
||||
{
|
||||
internal BoolLayerProperty()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
KeyframesSupported = false;
|
||||
RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new GeneralDataBindingConverter<bool>(), "Value");
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -12,7 +12,6 @@ namespace Artemis.Core
|
||||
internal ColorGradientLayerProperty()
|
||||
{
|
||||
KeyframesSupported = false;
|
||||
DataBindingsSupported = true;
|
||||
DefaultValue = new ColorGradient();
|
||||
|
||||
CurrentValueSet += OnCurrentValueSet;
|
||||
@ -20,7 +19,7 @@ namespace Artemis.Core
|
||||
|
||||
private void CreateDataBindingRegistrations()
|
||||
{
|
||||
ClearDataBindingProperties();
|
||||
DataBinding.ClearDataBindingProperties();
|
||||
if (CurrentValue == null)
|
||||
return;
|
||||
|
||||
@ -33,7 +32,7 @@ namespace Artemis.Core
|
||||
CurrentValue[stopIndex].Color = value;
|
||||
}
|
||||
|
||||
RegisterDataBindingProperty(() => CurrentValue[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}");
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue[stopIndex].Color, Setter, $"Color #{stopIndex + 1}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +70,7 @@ namespace Artemis.Core
|
||||
|
||||
private void SubscribedGradientOnPropertyChanged(object? sender, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
if (CurrentValue.Count != GetAllDataBindingRegistrations().Count)
|
||||
if (CurrentValue.Count != DataBinding.Properties.Count)
|
||||
CreateDataBindingRegistrations();
|
||||
}
|
||||
|
||||
@ -89,25 +88,4 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class ColorStopDataBindingConverter : DataBindingConverter<ColorGradient, SKColor>
|
||||
{
|
||||
public ColorStopDataBindingConverter()
|
||||
{
|
||||
SupportsInterpolate = true;
|
||||
SupportsSum = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SKColor Sum(SKColor a, SKColor b)
|
||||
{
|
||||
return a.Sum(b);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SKColor Interpolate(SKColor a, SKColor b, double progress)
|
||||
{
|
||||
return a.Interpolate(b, (float) progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ namespace Artemis.Core
|
||||
internal EnumLayerProperty()
|
||||
{
|
||||
KeyframesSupported = false;
|
||||
DataBindingsSupported = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
{
|
||||
internal FloatLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new FloatDataBindingConverter(), "Value");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -5,13 +5,17 @@
|
||||
{
|
||||
internal FloatRangeLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new FloatDataBindingConverter<FloatRange>(), "Start");
|
||||
RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new FloatDataBindingConverter<FloatRange>(), "End");
|
||||
|
||||
CurrentValueSet += OnCurrentValueSet;
|
||||
DefaultValue = new FloatRange();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, "Start");
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, "End");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||
{
|
||||
|
||||
@ -7,7 +7,12 @@ namespace Artemis.Core
|
||||
{
|
||||
internal IntLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new IntDataBindingConverter(), "Value");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -5,13 +5,17 @@
|
||||
{
|
||||
internal IntRangeLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new IntDataBindingConverter<IntRange>(), "Start");
|
||||
RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new IntDataBindingConverter<IntRange>(), "End");
|
||||
|
||||
CurrentValueSet += OnCurrentValueSet;
|
||||
DefaultValue = new IntRange();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, "Start");
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, "End");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||
{
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
internal LayerBrushReferenceLayerProperty()
|
||||
{
|
||||
KeyframesSupported = false;
|
||||
DataBindingsSupported = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -7,7 +7,12 @@ namespace Artemis.Core
|
||||
{
|
||||
internal SKColorLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new SKColorDataBindingConverter(), "Value");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -7,8 +7,13 @@ namespace Artemis.Core
|
||||
{
|
||||
internal SKPointLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue.X, value => CurrentValue = new SKPoint(value, CurrentValue.Y), new FloatDataBindingConverter<SKPoint>(), "X");
|
||||
RegisterDataBindingProperty(() => CurrentValue.Y, value => CurrentValue = new SKPoint(CurrentValue.X, value), new FloatDataBindingConverter<SKPoint>(), "Y");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.X, value => CurrentValue = new SKPoint(value, CurrentValue.Y), "X");
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Y, value => CurrentValue = new SKPoint(CurrentValue.X, value), "Y");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -7,8 +7,13 @@ namespace Artemis.Core
|
||||
{
|
||||
internal SKSizeLayerProperty()
|
||||
{
|
||||
RegisterDataBindingProperty(() => CurrentValue.Width, (value) => CurrentValue = new SKSize(value, CurrentValue.Height), new FloatDataBindingConverter<SKSize>(), "Width");
|
||||
RegisterDataBindingProperty(() => CurrentValue.Height, (value) => CurrentValue = new SKSize(CurrentValue.Width, value), new FloatDataBindingConverter<SKSize>(), "Height");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Width, (value) => CurrentValue = new SKSize(value, CurrentValue.Height), "Width");
|
||||
DataBinding.RegisterDataBindingProperty(() => CurrentValue.Height, (value) => CurrentValue = new SKSize(CurrentValue.Width, value), "Height");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
20
src/Artemis.Core/Events/Profiles/DataBindingEventArgs.cs
Normal file
20
src/Artemis.Core/Events/Profiles/DataBindingEventArgs.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides data for data binding events.
|
||||
/// </summary>
|
||||
public class DataBindingEventArgs : EventArgs
|
||||
{
|
||||
internal DataBindingEventArgs(IDataBinding dataBinding)
|
||||
{
|
||||
DataBinding = dataBinding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data binding this event is related to
|
||||
/// </summary>
|
||||
public IDataBinding DataBinding { get; }
|
||||
}
|
||||
}
|
||||
@ -1,25 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DataBinding<TLayerProperty, TProperty> : IDataBinding
|
||||
public class DataBinding<TLayerProperty> : IDataBinding
|
||||
{
|
||||
private TProperty _currentValue = default!;
|
||||
private readonly List<IDataBindingProperty> _properties = new();
|
||||
private bool _disposed;
|
||||
private TimeSpan _easingProgress;
|
||||
private TProperty _lastAppliedValue = default!;
|
||||
private TProperty _previousValue = default!;
|
||||
private bool _reapplyValue;
|
||||
private bool _isEnabled;
|
||||
|
||||
internal DataBinding(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
|
||||
internal DataBinding(LayerProperty<TLayerProperty> layerProperty)
|
||||
{
|
||||
LayerProperty = dataBindingRegistration.LayerProperty;
|
||||
Entity = new DataBindingEntity();
|
||||
LayerProperty = layerProperty;
|
||||
|
||||
ApplyRegistration(dataBindingRegistration);
|
||||
Script = new NodeScript<TProperty>(GetScriptName(), "The value to put into the data binding", LayerProperty.ProfileElement.Profile);
|
||||
Entity = new DataBindingEntity();
|
||||
Script = new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile);
|
||||
|
||||
Save();
|
||||
}
|
||||
@ -27,39 +26,23 @@ namespace Artemis.Core
|
||||
internal DataBinding(LayerProperty<TLayerProperty> layerProperty, DataBindingEntity entity)
|
||||
{
|
||||
LayerProperty = layerProperty;
|
||||
|
||||
Entity = entity;
|
||||
Script = new NodeScript<TProperty>(GetScriptName(), "The value to put into the data binding", LayerProperty.ProfileElement.Profile);
|
||||
Script = new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile);
|
||||
|
||||
// Load will add children so be initialized before that
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data binding registration this data binding is based upon
|
||||
/// </summary>
|
||||
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" />
|
||||
/// Gets the script used to populate the data binding
|
||||
/// </summary>
|
||||
public DataBindingConverter<TLayerProperty, TProperty>? Converter { get; private set; }
|
||||
|
||||
public NodeScript<TProperty> Script { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the easing time of the data binding
|
||||
/// </summary>
|
||||
public TimeSpan EasingTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets ors ets the easing function of the data binding
|
||||
/// </summary>
|
||||
public Easings.Functions EasingFunction { get; set; }
|
||||
public DataBindingNodeScript<TLayerProperty> Script { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data binding entity this data binding uses for persistent storage
|
||||
@ -67,38 +50,52 @@ namespace Artemis.Core
|
||||
public DataBindingEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value of the data binding
|
||||
/// Updates the pending values of this data binding
|
||||
/// </summary>
|
||||
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
|
||||
/// <returns></returns>
|
||||
public TProperty GetValue(TProperty baseValue)
|
||||
public void Update()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
|
||||
if (Converter == null)
|
||||
return baseValue;
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
|
||||
// TODO: Update the base node
|
||||
|
||||
Script.Run();
|
||||
|
||||
// If no easing is to be applied simple return whatever the current value is
|
||||
if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
|
||||
return Script.Result;
|
||||
|
||||
// If the value changed, update the current and previous values used for easing
|
||||
if (!Equals(Script.Result, _currentValue))
|
||||
ResetEasing(Script.Result);
|
||||
|
||||
// Apply interpolation between the previous and current value
|
||||
return GetInterpolatedValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the type of the target property of this data binding
|
||||
/// Registers a data binding property so that is available to the data binding system
|
||||
/// </summary>
|
||||
public Type? GetTargetType()
|
||||
/// <typeparam name="TProperty">The type of the layer property</typeparam>
|
||||
/// <param name="getter">The function to call to get the value of the property</param>
|
||||
/// <param name="setter">The action to call to set the value of the property</param>
|
||||
/// <param name="displayName">The display name of the data binding property</param>
|
||||
public DataBindingProperty<TProperty> RegisterDataBindingProperty<TProperty>(Func<TProperty> getter, Action<TProperty?> setter, string displayName)
|
||||
{
|
||||
return Registration?.Getter.Method.ReturnType;
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
if (Properties.Any(d => d.DisplayName == displayName))
|
||||
throw new ArtemisCoreException($"A databinding property named '{displayName}' is already registered.");
|
||||
|
||||
DataBindingProperty<TProperty> property = new(getter, setter, displayName);
|
||||
_properties.Add(property);
|
||||
|
||||
OnDataBindingPropertyRegistered();
|
||||
return property;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all data binding properties so they are no longer available to the data binding system
|
||||
/// </summary>
|
||||
public void ClearDataBindingProperties()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
|
||||
_properties.Clear();
|
||||
OnDataBindingPropertiesCleared();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -113,111 +110,78 @@ namespace Artemis.Core
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
if (Registration != null)
|
||||
Registration.DataBinding = null;
|
||||
|
||||
Script.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetEasing(TProperty value)
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingPropertyRegistered" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingPropertyRegistered()
|
||||
{
|
||||
_previousValue = GetInterpolatedValue();
|
||||
_currentValue = value;
|
||||
_easingProgress = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
private void ApplyRegistration(DataBindingRegistration<TLayerProperty, TProperty> dataBindingRegistration)
|
||||
{
|
||||
if (dataBindingRegistration == null)
|
||||
throw new ArgumentNullException(nameof(dataBindingRegistration));
|
||||
|
||||
dataBindingRegistration.DataBinding = this;
|
||||
Converter = dataBindingRegistration.Converter;
|
||||
Registration = dataBindingRegistration;
|
||||
|
||||
if (GetTargetType()!.IsValueType)
|
||||
{
|
||||
if (_currentValue == null)
|
||||
_currentValue = default!;
|
||||
if (_previousValue == null)
|
||||
_previousValue = default!;
|
||||
}
|
||||
|
||||
Converter?.Initialize(this);
|
||||
}
|
||||
|
||||
private TProperty GetInterpolatedValue()
|
||||
{
|
||||
if (_easingProgress == EasingTime || Converter == null || !Converter.SupportsInterpolate)
|
||||
return _currentValue;
|
||||
|
||||
double easingAmount = _easingProgress.TotalSeconds / EasingTime.TotalSeconds;
|
||||
return Converter.Interpolate(_previousValue, _currentValue, Easings.Interpolate(easingAmount, EasingFunction));
|
||||
DataBindingPropertyRegistered?.Invoke(this, new DataBindingEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the smoothing progress of the data binding
|
||||
/// Invokes the <see cref="DataBindingDisabled" /> event
|
||||
/// </summary>
|
||||
/// <param name="timeline">The timeline to apply during update</param>
|
||||
public void Update(Timeline timeline)
|
||||
protected virtual void OnDataBindingPropertiesCleared()
|
||||
{
|
||||
// Don't update data bindings if there is no delta, otherwise this creates an inconsistency between
|
||||
// data bindings with easing and data bindings without easing (the ones with easing will seemingly not update)
|
||||
if (timeline.Delta == TimeSpan.Zero || timeline.IsOverridden)
|
||||
return;
|
||||
DataBindingPropertiesCleared?.Invoke(this, new DataBindingEventArgs(this));
|
||||
}
|
||||
|
||||
UpdateWithDelta(timeline.Delta);
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingEnabled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingEnabled(DataBindingEventArgs e)
|
||||
{
|
||||
DataBindingEnabled?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingDisabled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingDisabled(DataBindingEventArgs e)
|
||||
{
|
||||
DataBindingDisabled?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private string GetScriptName()
|
||||
{
|
||||
return LayerProperty.PropertyDescription.Name ?? LayerProperty.Path;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateWithDelta(TimeSpan delta)
|
||||
public ILayerProperty BaseLayerProperty => LayerProperty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
_isEnabled = value;
|
||||
|
||||
// Data bindings cannot go back in time like brushes
|
||||
if (delta < TimeSpan.Zero)
|
||||
delta = TimeSpan.Zero;
|
||||
|
||||
_easingProgress = _easingProgress.Add(delta);
|
||||
if (_easingProgress > EasingTime)
|
||||
_easingProgress = EasingTime;
|
||||
|
||||
// Tell Apply() to apply a new value next call
|
||||
_reapplyValue = false;
|
||||
if (_isEnabled)
|
||||
OnDataBindingEnabled(new DataBindingEventArgs(this));
|
||||
else
|
||||
OnDataBindingDisabled(new DataBindingEventArgs(this));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReadOnlyCollection<IDataBindingProperty> Properties => _properties.AsReadOnly();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Apply()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
|
||||
if (Converter == null)
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
|
||||
// If Update() has not been called, reapply the previous value
|
||||
if (_reapplyValue)
|
||||
{
|
||||
Converter.ApplyValue(_lastAppliedValue);
|
||||
return;
|
||||
}
|
||||
|
||||
TProperty converterValue = Converter.GetValue();
|
||||
TProperty value = GetValue(converterValue);
|
||||
Converter.ApplyValue(value);
|
||||
|
||||
_lastAppliedValue = value;
|
||||
_reapplyValue = true;
|
||||
}
|
||||
|
||||
private string GetScriptName()
|
||||
{
|
||||
if (LayerProperty.GetAllDataBindingRegistrations().Count == 1)
|
||||
return LayerProperty.PropertyDescription.Name ?? LayerProperty.Path;
|
||||
return $"{LayerProperty.PropertyDescription.Name ?? LayerProperty.Path} - {Registration?.DisplayName}";
|
||||
Script.DataBindingExitNode.ApplyToDataBinding();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -227,6 +191,19 @@ namespace Artemis.Core
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingPropertyRegistered;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingPropertiesCleared;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingEnabled;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingDisabled;
|
||||
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -234,14 +211,6 @@ namespace Artemis.Core
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
|
||||
// General
|
||||
DataBindingRegistration<TLayerProperty, TProperty>? registration = LayerProperty.GetDataBindingRegistration<TProperty>(Entity.Identifier);
|
||||
if (registration != null)
|
||||
ApplyRegistration(registration);
|
||||
|
||||
EasingTime = Entity.EasingTime;
|
||||
EasingFunction = (Easings.Functions) Entity.EasingFunction;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -249,8 +218,8 @@ namespace Artemis.Core
|
||||
{
|
||||
Script.Dispose();
|
||||
Script = Entity.NodeScript != null
|
||||
? new NodeScript<TProperty>(GetScriptName(), "The value to put into the data binding", Entity.NodeScript, LayerProperty.ProfileElement.Profile)
|
||||
: new NodeScript<TProperty>(GetScriptName(), "The value to put into the data binding", LayerProperty.ProfileElement.Profile);
|
||||
? new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, Entity.NodeScript, LayerProperty.ProfileElement.Profile)
|
||||
: new DataBindingNodeScript<TLayerProperty>(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -259,17 +228,7 @@ namespace Artemis.Core
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
|
||||
if (!LayerProperty.Entity.DataBindingEntities.Contains(Entity))
|
||||
LayerProperty.Entity.DataBindingEntities.Add(Entity);
|
||||
|
||||
// Don't save an invalid state
|
||||
if (Registration != null)
|
||||
Entity.Identifier = Registration.DisplayName;
|
||||
|
||||
Entity.EasingTime = EasingTime;
|
||||
Entity.EasingFunction = (int) EasingFunction;
|
||||
|
||||
Script?.Save();
|
||||
Script.Save();
|
||||
Entity.NodeScript = Script?.Entity;
|
||||
}
|
||||
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
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 abstract class DataBindingConverter<TLayerProperty, TProperty> : IDataBindingConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the data binding this converter is applied to
|
||||
/// </summary>
|
||||
public DataBinding<TLayerProperty, TProperty>? DataBinding { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this data binding converter supports the <see cref="Sum" /> method
|
||||
/// </summary>
|
||||
public bool SupportsSum { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this data binding converter supports the <see cref="Interpolate" /> method
|
||||
/// </summary>
|
||||
public bool SupportsInterpolate { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of <paramref name="a" /> and <paramref name="b" />
|
||||
/// </summary>
|
||||
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)
|
||||
/// between <c>0.0</c> and <c>1.0</c> defined by the <paramref name="progress" />
|
||||
/// <para>Note: The progress may go be negative or go beyond <c>1.0</c> depending on the easing method used</para>
|
||||
/// </summary>
|
||||
/// <param name="a">The value to interpolate away from</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>
|
||||
/// <returns></returns>
|
||||
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 virtual void ApplyValue(TProperty value)
|
||||
{
|
||||
if (DataBinding?.Registration == null)
|
||||
throw new ArtemisCoreException("Data binding converter is not yet initialized");
|
||||
DataBinding.Registration.Setter(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current base value of the data binding
|
||||
/// </summary>
|
||||
public virtual TProperty GetValue()
|
||||
{
|
||||
if (DataBinding?.Registration == null)
|
||||
throw new ArtemisCoreException("Data binding converter is not yet initialized");
|
||||
return DataBinding.Registration.Getter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided object to a type of <typeparamref name="TProperty" />
|
||||
/// </summary>
|
||||
public virtual TProperty ConvertFromObject(object? source)
|
||||
{
|
||||
return (TProperty) Convert.ChangeType(source, typeof(TProperty))!;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (dataBinding.Registration == null)
|
||||
throw new ArtemisCoreException("Cannot initialize a data binding converter for a data binding without a registration");
|
||||
|
||||
DataBinding = dataBinding;
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type SupportedType => typeof(TProperty);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DataBindingProperty<TProperty> : IDataBindingProperty
|
||||
{
|
||||
internal DataBindingProperty(Func<TProperty> getter, Action<TProperty?> setter, string displayName)
|
||||
{
|
||||
Getter = getter ?? throw new ArgumentNullException(nameof(getter));
|
||||
Setter = setter ?? throw new ArgumentNullException(nameof(setter));
|
||||
DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the function to call to get the value of the property
|
||||
/// </summary>
|
||||
public Func<TProperty> Getter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the action to call to set the value of the property
|
||||
/// </summary>
|
||||
public Action<TProperty?> Setter { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DisplayName { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type ValueType => typeof(TProperty);
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? GetValue()
|
||||
{
|
||||
return Getter();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetValue(object? value)
|
||||
{
|
||||
if (value is TProperty match)
|
||||
Setter(match);
|
||||
else
|
||||
throw new ArgumentException("Value must match the type of the data binding registration", nameof(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DataBindingRegistration<TLayerProperty, TProperty> : IDataBindingRegistration
|
||||
{
|
||||
internal DataBindingRegistration(LayerProperty<TLayerProperty> layerProperty, DataBindingConverter<TLayerProperty, TProperty> converter,
|
||||
Func<TProperty> getter, Action<TProperty> setter, string displayName)
|
||||
{
|
||||
LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty));
|
||||
Converter = converter ?? throw new ArgumentNullException(nameof(converter));
|
||||
Getter = getter ?? throw new ArgumentNullException(nameof(getter));
|
||||
Setter = setter ?? throw new ArgumentNullException(nameof(setter));
|
||||
DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName));
|
||||
}
|
||||
|
||||
/// <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 function to call to get the value of the property
|
||||
/// </summary>
|
||||
public Func<TProperty> Getter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the action to call to set the value of the property
|
||||
/// </summary>
|
||||
public Action<TProperty> Setter { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DisplayName { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type ValueType => typeof(TProperty);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data binding created using this registration
|
||||
/// </summary>
|
||||
public DataBinding<TLayerProperty, TProperty>? DataBinding { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDataBinding? GetDataBinding()
|
||||
{
|
||||
return DataBinding;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDataBinding? CreateDataBinding()
|
||||
{
|
||||
if (DataBinding != null)
|
||||
return DataBinding;
|
||||
|
||||
DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.Identifier == DisplayName);
|
||||
if (dataBinding == null)
|
||||
return null;
|
||||
|
||||
DataBinding = new DataBinding<TLayerProperty, TProperty>(LayerProperty, dataBinding);
|
||||
return DataBinding;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearDataBinding()
|
||||
{
|
||||
if (DataBinding == null)
|
||||
return;
|
||||
|
||||
// The related entity is left behind, just in case the data binding is added back later
|
||||
LayerProperty.DisableDataBinding(DataBinding);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? GetValue()
|
||||
{
|
||||
return Getter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Core.Modules;
|
||||
|
||||
namespace Artemis.Core
|
||||
@ -7,22 +8,51 @@ namespace Artemis.Core
|
||||
/// 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, IDisposable
|
||||
public interface IDataBinding : IStorageModel, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the smoothing progress of the data binding and recalculates the value next <see cref="Apply" /> call
|
||||
/// Gets the layer property the data binding is applied to
|
||||
/// </summary>
|
||||
/// <param name="delta">The delta to apply during update</param>
|
||||
void UpdateWithDelta(TimeSpan delta);
|
||||
ILayerProperty BaseLayerProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies the data binding to the layer property
|
||||
/// Gets a list of sub-properties this data binding applies to
|
||||
/// </summary>
|
||||
ReadOnlyCollection<IDataBindingProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the data binding is enabled or not
|
||||
/// </summary>
|
||||
bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies the pending value of the data binding to the property
|
||||
/// </summary>
|
||||
void Apply();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// If the data binding is enabled, loads the node script for that data binding
|
||||
/// </summary>
|
||||
void LoadNodeScript();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a data binding property has been added
|
||||
/// </summary>
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingPropertyRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when all data binding properties have been removed
|
||||
/// </summary>
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingPropertiesCleared;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a data binding has been enabled
|
||||
/// </summary>
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a data binding has been disabled
|
||||
/// </summary>
|
||||
public event EventHandler<DataBindingEventArgs>? DataBindingDisabled;
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ namespace Artemis.Core
|
||||
/// <summary>
|
||||
/// Represents a data binding registration
|
||||
/// </summary>
|
||||
public interface IDataBindingRegistration
|
||||
public interface IDataBindingProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the display name of the data binding registration
|
||||
@ -18,25 +18,15 @@ namespace Artemis.Core
|
||||
Type ValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the data binding applied using this registration
|
||||
/// </summary>
|
||||
public IDataBinding? GetDataBinding();
|
||||
|
||||
/// <summary>
|
||||
/// If found, creates a data binding from storage
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IDataBinding? CreateDataBinding();
|
||||
|
||||
/// <summary>
|
||||
/// If present, removes the current data binding
|
||||
/// </summary>
|
||||
void ClearDataBinding();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the data binding
|
||||
/// Gets the value of the property this registration points to
|
||||
/// </summary>
|
||||
/// <returns>A value matching the type of <see cref="ValueType" /></returns>
|
||||
object? GetValue();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of the property this registration points to
|
||||
/// </summary>
|
||||
/// <param name="value">A value matching the type of <see cref="ValueType" /></param>
|
||||
void SetValue(object? value);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
|
||||
namespace Artemis.Core
|
||||
@ -23,6 +22,21 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
LayerPropertyGroup LayerPropertyGroup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data binding of this property
|
||||
/// </summary>
|
||||
IDataBinding BaseDataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the layer has any data binding properties
|
||||
/// </summary>
|
||||
public bool HasDataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether data bindings are supported on this type of property
|
||||
/// </summary>
|
||||
public bool DataBindingsSupported { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique path of the property on the layer
|
||||
/// </summary>
|
||||
@ -47,11 +61,6 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list off all data binding registrations
|
||||
/// </summary>
|
||||
List<IDataBindingRegistration> GetAllDataBindingRegistrations();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load and add the provided keyframe entity to the layer property
|
||||
/// </summary>
|
||||
@ -70,6 +79,12 @@ namespace Artemis.Core
|
||||
/// <param name="timeline">The timeline to apply to the property</param>
|
||||
void Update(Timeline timeline);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates just the data binding instead of the entire layer
|
||||
/// </summary>
|
||||
void UpdateDataBinding();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the layer property is disposed
|
||||
/// </summary>
|
||||
@ -104,25 +119,5 @@ namespace Artemis.Core
|
||||
/// Occurs when a keyframe was removed from the layer property
|
||||
/// </summary>
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a data binding property has been added
|
||||
/// </summary>
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertyRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when all data binding properties have been removed
|
||||
/// </summary>
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertiesCleared;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a data binding has been enabled
|
||||
/// </summary>
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a data binding has been disabled
|
||||
/// </summary>
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingDisabled;
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@ namespace Artemis.Core
|
||||
Path = null!;
|
||||
Entity = null!;
|
||||
PropertyDescription = null!;
|
||||
DataBinding = null!;
|
||||
|
||||
CurrentValue = default!;
|
||||
DefaultValue = default!;
|
||||
@ -57,93 +58,10 @@ namespace Artemis.Core
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
foreach (IDataBinding dataBinding in _dataBindings)
|
||||
dataBinding.Dispose();
|
||||
|
||||
DataBinding.Dispose();
|
||||
Disposed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="Updated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnUpdated()
|
||||
{
|
||||
Updated?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="CurrentValueSet" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnCurrentValueSet()
|
||||
{
|
||||
CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="VisibilityChanged" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnVisibilityChanged()
|
||||
{
|
||||
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyframesToggled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnKeyframesToggled()
|
||||
{
|
||||
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyframeAdded" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnKeyframeAdded()
|
||||
{
|
||||
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyframeRemoved" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnKeyframeRemoved()
|
||||
{
|
||||
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingPropertyRegistered" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingPropertyRegistered()
|
||||
{
|
||||
DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingDisabled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingPropertiesCleared()
|
||||
{
|
||||
DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingEnabled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e)
|
||||
{
|
||||
DataBindingEnabled?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="DataBindingDisabled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e)
|
||||
{
|
||||
DataBindingDisabled?.Invoke(this, e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PropertyDescriptionAttribute PropertyDescription { get; internal set; }
|
||||
|
||||
@ -162,7 +80,16 @@ namespace Artemis.Core
|
||||
CurrentValue = BaseValue;
|
||||
|
||||
UpdateKeyframes(timeline);
|
||||
UpdateDataBindings(timeline);
|
||||
UpdateDataBinding();
|
||||
|
||||
// UpdateDataBinding called OnUpdated()
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UpdateDataBinding()
|
||||
{
|
||||
DataBinding.Update();
|
||||
DataBinding.Apply();
|
||||
|
||||
OnUpdated();
|
||||
}
|
||||
@ -174,39 +101,6 @@ namespace Artemis.Core
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? Disposed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? Updated;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? CurrentValueSet;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? VisibilityChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframesToggled;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertyRegistered;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingPropertiesCleared;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingEnabled;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? DataBindingDisabled;
|
||||
|
||||
#region Hierarchy
|
||||
|
||||
private bool _isHidden;
|
||||
@ -480,137 +374,19 @@ namespace Artemis.Core
|
||||
|
||||
#region Data bindings
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
internal readonly List<IDataBindingRegistration> _dataBindingRegistrations = new();
|
||||
|
||||
internal readonly List<IDataBinding> _dataBindings = new();
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether data bindings are supported on this type of property
|
||||
/// Gets the data binding of this property
|
||||
/// </summary>
|
||||
public bool DataBindingsSupported { get; protected internal set; } = true;
|
||||
public DataBinding<T> DataBinding { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the layer has any active data bindings
|
||||
/// </summary>
|
||||
public bool HasDataBinding => GetAllDataBindingRegistrations().Any(r => r.GetDataBinding() != null);
|
||||
/// <inheritdoc />
|
||||
public bool DataBindingsSupported => DataBinding.Properties.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a data binding registration by the display name used to register it
|
||||
/// <para>Note: The expression must exactly match the one used to register the data binding</para>
|
||||
/// </summary>
|
||||
public DataBindingRegistration<T, TProperty>? GetDataBindingRegistration<TProperty>(string identifier)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
/// <inheritdoc />
|
||||
public IDataBinding BaseDataBinding => DataBinding;
|
||||
|
||||
IDataBindingRegistration? match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration<T, TProperty> registration &&
|
||||
registration.DisplayName == identifier);
|
||||
return (DataBindingRegistration<T, TProperty>?) match;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list containing all data binding registrations of this layer property
|
||||
/// </summary>
|
||||
/// <returns>A list containing all data binding registrations of this layer property</returns>
|
||||
public List<IDataBindingRegistration> GetAllDataBindingRegistrations()
|
||||
{
|
||||
return _dataBindingRegistrations;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a data binding property so that is available to the data binding system
|
||||
/// </summary>
|
||||
/// <typeparam name="TProperty">The type of the layer property</typeparam>
|
||||
/// <param name="getter">The function to call to get the value of the property</param>
|
||||
/// <param name="setter">The action to call to set the value of the property</param>
|
||||
/// <param name="converter">The converter to use while applying the data binding</param>
|
||||
/// <param name="displayName">The display name of the data binding property</param>
|
||||
public DataBindingRegistration<T, TProperty> RegisterDataBindingProperty<TProperty>(Func<TProperty> getter, Action<TProperty> setter, DataBindingConverter<T, TProperty> converter,
|
||||
string displayName)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
if (_dataBindingRegistrations.Any(d => d.DisplayName == displayName))
|
||||
throw new ArtemisCoreException($"A databinding property named '{displayName}' is already registered.");
|
||||
|
||||
DataBindingRegistration<T, TProperty> registration = new(this, converter, getter, setter, displayName);
|
||||
_dataBindingRegistrations.Add(registration);
|
||||
|
||||
// If not yet initialized, load the data binding related to the registration if available
|
||||
if (_isInitialized)
|
||||
{
|
||||
IDataBinding? dataBinding = registration.CreateDataBinding();
|
||||
if (dataBinding != null)
|
||||
_dataBindings.Add(dataBinding);
|
||||
}
|
||||
|
||||
OnDataBindingPropertyRegistered();
|
||||
return registration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all data binding properties so they are no longer available to the data binding system
|
||||
/// </summary>
|
||||
public void ClearDataBindingProperties()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
|
||||
foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations)
|
||||
dataBindingRegistration.ClearDataBinding();
|
||||
_dataBindingRegistrations.Clear();
|
||||
|
||||
OnDataBindingPropertiesCleared();
|
||||
}
|
||||
|
||||
/// <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 (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
|
||||
if (dataBindingRegistration.LayerProperty != this)
|
||||
throw new ArtemisCoreException("Cannot enable a data binding using a data binding registration of a different layer property");
|
||||
if (dataBindingRegistration.DataBinding != null)
|
||||
throw new ArtemisCoreException("Provided data binding registration already has an enabled data binding");
|
||||
|
||||
DataBinding<T, TProperty> dataBinding = new(dataBindingRegistration);
|
||||
_dataBindings.Add(dataBinding);
|
||||
|
||||
OnDataBindingEnabled(new LayerPropertyEventArgs(dataBinding.LayerProperty));
|
||||
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)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("LayerProperty");
|
||||
|
||||
_dataBindings.Remove(dataBinding);
|
||||
|
||||
if (dataBinding.Registration != null)
|
||||
dataBinding.Registration.DataBinding = null;
|
||||
dataBinding.Dispose();
|
||||
OnDataBindingDisabled(new LayerPropertyEventArgs(dataBinding.LayerProperty));
|
||||
}
|
||||
|
||||
private void UpdateDataBindings(Timeline timeline)
|
||||
{
|
||||
foreach (IDataBinding dataBinding in _dataBindings)
|
||||
{
|
||||
dataBinding.Update(timeline);
|
||||
dataBinding.Apply();
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool HasDataBinding => DataBinding.IsEnabled;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -694,6 +470,7 @@ namespace Artemis.Core
|
||||
Entity = entity ?? throw new ArgumentNullException(nameof(entity));
|
||||
PropertyDescription = description ?? throw new ArgumentNullException(nameof(description));
|
||||
IsLoadedFromStorage = fromStorage;
|
||||
DataBinding = new DataBinding<T>(this);
|
||||
|
||||
if (PropertyDescription.DisableKeyframes)
|
||||
KeyframesSupported = false;
|
||||
@ -737,13 +514,7 @@ namespace Artemis.Core
|
||||
// ignored for now
|
||||
}
|
||||
|
||||
_dataBindings.Clear();
|
||||
foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations)
|
||||
{
|
||||
IDataBinding? dataBinding = dataBindingRegistration.CreateDataBinding();
|
||||
if (dataBinding != null)
|
||||
_dataBindings.Add(dataBinding);
|
||||
}
|
||||
DataBinding.Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -762,9 +533,8 @@ namespace Artemis.Core
|
||||
Entity.KeyframeEntities.Clear();
|
||||
Entity.KeyframeEntities.AddRange(Keyframes.Select(k => k.GetKeyframeEntity()));
|
||||
|
||||
Entity.DataBindingEntities.Clear();
|
||||
foreach (IDataBinding dataBinding in _dataBindings)
|
||||
dataBinding.Save();
|
||||
DataBinding.Save();
|
||||
Entity.DataBinding = DataBinding.Entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -775,5 +545,79 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler? Disposed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? Updated;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? CurrentValueSet;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? VisibilityChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframesToggled;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframeAdded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<LayerPropertyEventArgs>? KeyframeRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="Updated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnUpdated()
|
||||
{
|
||||
Updated?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="CurrentValueSet" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnCurrentValueSet()
|
||||
{
|
||||
CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="VisibilityChanged" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnVisibilityChanged()
|
||||
{
|
||||
VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyframesToggled" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnKeyframesToggled()
|
||||
{
|
||||
KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyframeAdded" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnKeyframeAdded()
|
||||
{
|
||||
KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="KeyframeRemoved" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnKeyframeRemoved()
|
||||
{
|
||||
KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -113,11 +113,8 @@ namespace Artemis.Core
|
||||
? new NodeScript<bool>($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", RenderElementEntity.NodeScript, Profile)
|
||||
: new NodeScript<bool>($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", Profile);
|
||||
|
||||
foreach (ILayerProperty layerProperty in GetAllLayerProperties())
|
||||
{
|
||||
foreach (IDataBindingRegistration dataBindingRegistration in layerProperty.GetAllDataBindingRegistrations())
|
||||
dataBindingRegistration.GetDataBinding()?.LoadNodeScript();
|
||||
}
|
||||
foreach (ILayerProperty layerProperty in GetAllLayerProperties())
|
||||
layerProperty.BaseDataBinding.LoadNodeScript();
|
||||
}
|
||||
|
||||
internal void OnLayerEffectsUpdated()
|
||||
|
||||
36
src/Artemis.Core/VisualScripting/DataBindingNodeScript.cs
Normal file
36
src/Artemis.Core/VisualScripting/DataBindingNodeScript.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Artemis.Core.Internal;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
public class DataBindingNodeScript<TLayerProperty> : NodeScript
|
||||
{
|
||||
#region Properties & Fields
|
||||
|
||||
internal DataBindingExitNode<TLayerProperty> DataBindingExitNode { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type ResultType => typeof(object);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataBindingNodeScript(string name, string description, DataBinding<TLayerProperty> dataBinding, object? context = null)
|
||||
: base(name, description, context)
|
||||
{
|
||||
DataBindingExitNode = new DataBindingExitNode<TLayerProperty>(dataBinding);
|
||||
ExitNode = DataBindingExitNode;
|
||||
AddNode(ExitNode);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataBindingNodeScript(string name, string description, DataBinding<TLayerProperty> dataBinding, NodeScriptEntity entity, object? context = null)
|
||||
: base(name, description, entity, context)
|
||||
{
|
||||
DataBindingExitNode = new DataBindingExitNode<TLayerProperty>(dataBinding);
|
||||
ExitNode = DataBindingExitNode;
|
||||
AddNode(ExitNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,7 +96,7 @@ namespace Artemis.Core
|
||||
|
||||
private void Evaluate()
|
||||
{
|
||||
Value = ConnectedTo.Count > 0 ? ConnectedTo[0].PinValue : default;
|
||||
Value = ConnectedTo.Count > 0 ? ConnectedTo[0].PinValue : Type.GetDefault();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Artemis.Core.Internal
|
||||
{
|
||||
internal class DataBindingExitNode<TLayerProperty> : Node, IExitNode
|
||||
{
|
||||
private readonly Dictionary<IDataBindingProperty, InputPin> _propertyPins = new();
|
||||
private readonly Dictionary<IDataBindingProperty, object> _propertyValues = new();
|
||||
|
||||
public DataBinding<TLayerProperty> DataBinding { get; }
|
||||
|
||||
public DataBindingExitNode(DataBinding<TLayerProperty> dataBinding) : base(dataBinding.LayerProperty.PropertyDescription.Name ?? "", "")
|
||||
{
|
||||
DataBinding = dataBinding;
|
||||
DataBinding.DataBindingPropertiesCleared += DataBindingOnDataBindingPropertiesCleared;
|
||||
DataBinding.DataBindingPropertyRegistered += DataBindingOnDataBindingPropertyRegistered;
|
||||
|
||||
CreateInputPins();
|
||||
}
|
||||
|
||||
public override bool IsExitNode => true;
|
||||
|
||||
public override void Evaluate()
|
||||
{
|
||||
foreach (var (property, inputPin) in _propertyPins)
|
||||
{
|
||||
if (inputPin.ConnectedTo.Any())
|
||||
_propertyValues[property] = inputPin.Value;
|
||||
else
|
||||
_propertyValues.Remove(property);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyToDataBinding()
|
||||
{
|
||||
foreach (var (property, pendingValue) in _propertyValues)
|
||||
property.SetValue(pendingValue);
|
||||
}
|
||||
|
||||
private void ClearInputPins()
|
||||
{
|
||||
while (Pins.Any())
|
||||
RemovePin((Pin) Pins.First());
|
||||
|
||||
_propertyPins.Clear();
|
||||
_propertyValues.Clear();
|
||||
}
|
||||
|
||||
private void CreateInputPins()
|
||||
{
|
||||
ClearInputPins();
|
||||
|
||||
foreach (IDataBindingProperty property in DataBinding.Properties)
|
||||
_propertyPins.Add(property, CreateInputPin(property.ValueType, property.DisplayName));
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DataBindingOnDataBindingPropertyRegistered(object? sender, DataBindingEventArgs e)
|
||||
{
|
||||
CreateInputPins();
|
||||
}
|
||||
|
||||
private void DataBindingOnDataBindingPropertiesCleared(object? sender, DataBindingEventArgs e)
|
||||
{
|
||||
ClearInputPins();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,10 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
using Artemis.Storage.Entities.Profile.Nodes;
|
||||
|
||||
namespace Artemis.Storage.Entities.Profile.DataBindings
|
||||
{
|
||||
public class DataBindingEntity
|
||||
{
|
||||
public string Identifier { get; set; }
|
||||
public TimeSpan EasingTime { get; set; }
|
||||
public int EasingFunction { get; set; }
|
||||
|
||||
public NodeScriptEntity NodeScript { get; set; }
|
||||
}
|
||||
}
|
||||
@ -8,16 +8,15 @@ namespace Artemis.Storage.Entities.Profile
|
||||
public PropertyEntity()
|
||||
{
|
||||
KeyframeEntities = new List<KeyframeEntity>();
|
||||
DataBindingEntities = new List<DataBindingEntity>();
|
||||
}
|
||||
|
||||
public string FeatureId { get; set; }
|
||||
public string Path { get; set; }
|
||||
public DataBindingEntity DataBinding { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
public bool KeyframesEnabled { get; set; }
|
||||
|
||||
public List<KeyframeEntity> KeyframeEntities { get; set; }
|
||||
public List<DataBindingEntity> DataBindingEntities { get; set; }
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,11 @@ namespace Artemis.UI.Shared
|
||||
/// </summary>
|
||||
public LayerProperty<T> LayerProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether the layer property should be enabled
|
||||
/// </summary>
|
||||
public bool IsEnabled => !LayerProperty.HasDataBinding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the profile editor service
|
||||
/// </summary>
|
||||
@ -85,8 +90,8 @@ namespace Artemis.UI.Shared
|
||||
{
|
||||
LayerProperty.Updated += LayerPropertyOnUpdated;
|
||||
LayerProperty.CurrentValueSet += LayerPropertyOnUpdated;
|
||||
LayerProperty.DataBindingEnabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBindingDisabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingEnabled += OnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingDisabled += OnDataBindingChange;
|
||||
UpdateInputValue();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
@ -96,8 +101,8 @@ namespace Artemis.UI.Shared
|
||||
{
|
||||
LayerProperty.Updated -= LayerPropertyOnUpdated;
|
||||
LayerProperty.CurrentValueSet -= LayerPropertyOnUpdated;
|
||||
LayerProperty.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingEnabled -= OnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingDisabled -= OnDataBindingChange;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
@ -194,8 +199,9 @@ namespace Artemis.UI.Shared
|
||||
UpdateInputValue();
|
||||
}
|
||||
|
||||
private void LayerPropertyOnDataBindingChange(object? sender, LayerPropertyEventArgs e)
|
||||
private void OnDataBindingChange(object? sender, DataBindingEventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
OnDataBindingsChanged();
|
||||
}
|
||||
|
||||
|
||||
@ -37,9 +37,9 @@ namespace Artemis.UI.Shared.Services
|
||||
RenderProfileElement? SelectedProfileElement { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently selected data binding property
|
||||
/// Gets the currently selected data binding
|
||||
/// </summary>
|
||||
ILayerProperty? SelectedDataBinding { get; }
|
||||
IDataBinding? SelectedDataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current time
|
||||
@ -89,10 +89,10 @@ namespace Artemis.UI.Shared.Services
|
||||
void SaveSelectedProfileElement();
|
||||
|
||||
/// <summary>
|
||||
/// Changes the selected data binding property
|
||||
/// Changes the selected data binding
|
||||
/// </summary>
|
||||
/// <param name="layerProperty">The data binding property to select</param>
|
||||
void ChangeSelectedDataBinding(ILayerProperty? layerProperty);
|
||||
/// <param name="dataBinding">The data binding to select</param>
|
||||
void ChangeSelectedDataBinding(IDataBinding? dataBinding);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the profile preview, forcing UI-elements to re-render
|
||||
@ -114,7 +114,7 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <summary>
|
||||
/// Registers a new property input view model used in the profile editor for the generic type defined in
|
||||
/// <see cref="PropertyInputViewModel{T}" />
|
||||
/// <para>Note: Registration will remove itself on plugin disable so you don't have to</para>
|
||||
/// <para>Note: DataBindingProperty will remove itself on plugin disable so you don't have to</para>
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
@ -123,7 +123,7 @@ namespace Artemis.UI.Shared.Services
|
||||
/// <summary>
|
||||
/// Registers a new property input view model used in the profile editor for the generic type defined in
|
||||
/// <see cref="PropertyInputViewModel{T}" />
|
||||
/// <para>Note: Registration will remove itself on plugin disable so you don't have to</para>
|
||||
/// <para>Note: DataBindingProperty will remove itself on plugin disable so you don't have to</para>
|
||||
/// </summary>
|
||||
/// <param name="viewModelType"></param>
|
||||
/// <param name="plugin"></param>
|
||||
|
||||
@ -115,7 +115,7 @@ namespace Artemis.UI.Shared.Services
|
||||
// Trigger selected data binding change
|
||||
if (SelectedDataBinding != null)
|
||||
{
|
||||
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.Path);
|
||||
SelectedDataBinding = SelectedProfileElement?.GetAllLayerProperties().FirstOrDefault(p => p.Path == SelectedDataBinding.BaseLayerProperty.Path)?.BaseDataBinding;
|
||||
OnSelectedDataBindingChanged();
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@ namespace Artemis.UI.Shared.Services
|
||||
public ProfileConfiguration? SelectedProfileConfiguration { get; private set; }
|
||||
public Profile? SelectedProfile => SelectedProfileConfiguration?.Profile;
|
||||
public RenderProfileElement? SelectedProfileElement { get; private set; }
|
||||
public ILayerProperty? SelectedDataBinding { get; private set; }
|
||||
public IDataBinding? SelectedDataBinding { get; private set; }
|
||||
|
||||
public TimeSpan CurrentTime
|
||||
{
|
||||
@ -303,9 +303,9 @@ namespace Artemis.UI.Shared.Services
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeSelectedDataBinding(ILayerProperty? layerProperty)
|
||||
public void ChangeSelectedDataBinding(IDataBinding? dataBinding)
|
||||
{
|
||||
SelectedDataBinding = layerProperty;
|
||||
SelectedDataBinding = dataBinding;
|
||||
OnSelectedDataBindingChanged();
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
|
||||
@ -8,14 +6,10 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class BoolPropertyInputViewModel : PropertyInputViewModel<bool>
|
||||
{
|
||||
private List<IDataBindingRegistration> _registrations;
|
||||
|
||||
public BoolPropertyInputViewModel(LayerProperty<bool> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService)
|
||||
{
|
||||
_registrations = layerProperty.GetAllDataBindingRegistrations();
|
||||
}
|
||||
|
||||
public bool IsEnabled => _registrations.Any(r => r.GetDataBinding() != null);
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors"
|
||||
xmlns:layerBrush="clr-namespace:Artemis.Core.LayerBrushes;assembly=Artemis.Core"
|
||||
xmlns:propertyInput="clr-namespace:Artemis.UI.DefaultTypes.PropertyInput"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
|
||||
@ -13,8 +13,8 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
|
||||
{
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IPluginManagementService _pluginManagementService;
|
||||
private BindableCollection<LayerBrushDescriptor> _descriptors;
|
||||
|
||||
public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty, IProfileEditorService profileEditorService, IPluginManagementService pluginManagementService,
|
||||
@ -51,13 +51,11 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
layer.ChangeLayerBrush(SelectedDescriptor);
|
||||
if (layer.LayerBrush?.Presets != null && layer.LayerBrush.Presets.Any())
|
||||
{
|
||||
Execute.PostToUIThread(async () =>
|
||||
{
|
||||
await Task.Delay(400);
|
||||
await _dialogService.ShowDialogAt<LayerBrushPresetViewModel>("LayerProperties", new Dictionary<string, object> {{"layerBrush", layer.LayerBrush}});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,19 +8,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class FloatPropertyInputViewModel : PropertyInputViewModel<float>
|
||||
{
|
||||
private readonly DataBindingRegistration<float, float> _registration;
|
||||
|
||||
public FloatPropertyInputViewModel(LayerProperty<float> layerProperty, IProfileEditorService profileEditorService, IModelValidator<FloatPropertyInputViewModel> validator)
|
||||
: base(layerProperty, profileEditorService, validator)
|
||||
{
|
||||
_registration = layerProperty.GetDataBindingRegistration<float>("Value");
|
||||
}
|
||||
|
||||
public bool IsEnabled => _registration.DataBinding == null;
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
Min="{Binding Start}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsEndEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -9,15 +9,10 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class FloatRangePropertyInputViewModel : PropertyInputViewModel<FloatRange>
|
||||
{
|
||||
private readonly DataBindingRegistration<FloatRange, float> _startRegistration;
|
||||
private readonly DataBindingRegistration<FloatRange, float> _endRegistration;
|
||||
|
||||
public FloatRangePropertyInputViewModel(LayerProperty<FloatRange> layerProperty,
|
||||
IProfileEditorService profileEditorService,
|
||||
IModelValidator<FloatRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
|
||||
{
|
||||
_startRegistration = layerProperty.GetDataBindingRegistration<float>("Start");
|
||||
_endRegistration = layerProperty.GetDataBindingRegistration<float>("End");
|
||||
}
|
||||
|
||||
public float Start
|
||||
@ -48,20 +43,12 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsStartEnabled => _startRegistration.DataBinding == null;
|
||||
public bool IsEndEnabled => _endRegistration.DataBinding == null;
|
||||
|
||||
protected override void OnInputValueChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(Start));
|
||||
NotifyOfPropertyChange(nameof(End));
|
||||
}
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsStartEnabled));
|
||||
NotifyOfPropertyChange(nameof(IsEndEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public class FloatRangePropertyInputViewModelValidator : AbstractValidator<FloatRangePropertyInputViewModel>
|
||||
|
||||
@ -8,19 +8,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class IntPropertyInputViewModel : PropertyInputViewModel<int>
|
||||
{
|
||||
private readonly DataBindingRegistration<int, int> _registration;
|
||||
|
||||
public IntPropertyInputViewModel(LayerProperty<int> layerProperty, IProfileEditorService profileEditorService, IModelValidator<IntPropertyInputViewModel> validator)
|
||||
: base(layerProperty, profileEditorService, validator)
|
||||
{
|
||||
_registration = layerProperty.GetDataBindingRegistration<int>("Value");
|
||||
}
|
||||
|
||||
public bool IsEnabled => _registration.DataBinding == null;
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsStartEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Margin="5 0" VerticalAlignment="Bottom">-</TextBlock>
|
||||
<shared:DraggableFloat ToolTip="End"
|
||||
Value="{Binding End}"
|
||||
@ -27,7 +27,7 @@
|
||||
Min="{Binding Start}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsEndEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -9,15 +9,10 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class IntRangePropertyInputViewModel : PropertyInputViewModel<IntRange>
|
||||
{
|
||||
private readonly DataBindingRegistration<IntRange, int> _startRegistration;
|
||||
private readonly DataBindingRegistration<IntRange, int> _endRegistration;
|
||||
|
||||
public IntRangePropertyInputViewModel(LayerProperty<IntRange> layerProperty,
|
||||
IProfileEditorService profileEditorService,
|
||||
IModelValidator<IntRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
|
||||
{
|
||||
_startRegistration = layerProperty.GetDataBindingRegistration<int>("Start");
|
||||
_endRegistration = layerProperty.GetDataBindingRegistration<int>("End");
|
||||
}
|
||||
|
||||
public int Start
|
||||
@ -48,20 +43,12 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsStartEnabled => _startRegistration.DataBinding == null;
|
||||
public bool IsEndEnabled => _endRegistration.DataBinding == null;
|
||||
|
||||
protected override void OnInputValueChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(Start));
|
||||
NotifyOfPropertyChange(nameof(End));
|
||||
}
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsStartEnabled));
|
||||
NotifyOfPropertyChange(nameof(IsEndEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public class IntRangePropertyInputViewModelValidator : AbstractValidator<IntRangePropertyInputViewModel>
|
||||
|
||||
@ -17,10 +17,10 @@
|
||||
<artemis:ColorPicker Width="132"
|
||||
Margin="0 -2 0 3"
|
||||
Padding="0 -1"
|
||||
Color="{Binding InputValue, Converter={StaticResource SKColorToColorConverter}}"
|
||||
Color="{Binding InputValue, Converter={StaticResource SKColorToColorConverter}}"
|
||||
IsEnabled="{Binding IsEnabled}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"/>
|
||||
DragEnded="{s:Action InputDragEnded}" />
|
||||
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -7,18 +7,8 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class SKColorPropertyInputViewModel : PropertyInputViewModel<SKColor>
|
||||
{
|
||||
private readonly DataBindingRegistration<SKColor, SKColor> _registration;
|
||||
|
||||
public SKColorPropertyInputViewModel(LayerProperty<SKColor> layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService)
|
||||
{
|
||||
_registration = layerProperty.GetDataBindingRegistration<SKColor>("Value");
|
||||
}
|
||||
|
||||
public bool IsEnabled => _registration.DataBinding == null;
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsXEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock>
|
||||
<shared:DraggableFloat ToolTip="Y-coordinate (vertical)"
|
||||
Value="{Binding Y}"
|
||||
@ -27,7 +27,7 @@
|
||||
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsYEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -10,14 +10,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class SKPointPropertyInputViewModel : PropertyInputViewModel<SKPoint>
|
||||
{
|
||||
private readonly DataBindingRegistration<SKPoint, float> _xRegistration;
|
||||
private readonly DataBindingRegistration<SKPoint, float> _yRegistration;
|
||||
|
||||
public SKPointPropertyInputViewModel(LayerProperty<SKPoint> layerProperty, IProfileEditorService profileEditorService,
|
||||
IModelValidator<SKPointPropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
|
||||
{
|
||||
_xRegistration = layerProperty.GetDataBindingRegistration<float>("X");
|
||||
_yRegistration = layerProperty.GetDataBindingRegistration<float>("Y");
|
||||
}
|
||||
|
||||
public float X
|
||||
@ -32,20 +27,12 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
set => InputValue = new SKPoint(X, value);
|
||||
}
|
||||
|
||||
public bool IsXEnabled => _xRegistration.DataBinding == null;
|
||||
public bool IsYEnabled => _yRegistration.DataBinding == null;
|
||||
|
||||
protected override void OnInputValueChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(X));
|
||||
NotifyOfPropertyChange(nameof(Y));
|
||||
}
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsXEnabled));
|
||||
NotifyOfPropertyChange(nameof(IsYEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public class SKPointPropertyInputViewModelValidator : AbstractValidator<SKPointPropertyInputViewModel>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsHeightEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Margin="5 0" VerticalAlignment="Bottom">,</TextBlock>
|
||||
<shared:DraggableFloat ToolTip="Width"
|
||||
Value="{Binding Width}"
|
||||
@ -27,7 +27,7 @@
|
||||
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
|
||||
DragStarted="{s:Action InputDragStarted}"
|
||||
DragEnded="{s:Action InputDragEnded}"
|
||||
IsEnabled="{Binding IsWidthEnabled}" />
|
||||
IsEnabled="{Binding IsEnabled}" />
|
||||
<TextBlock Width="25" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -12,14 +12,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
{
|
||||
public class SKSizePropertyInputViewModel : PropertyInputViewModel<SKSize>
|
||||
{
|
||||
private readonly DataBindingRegistration<SKSize, float> _heightRegistration;
|
||||
private readonly DataBindingRegistration<SKSize, float> _widthRegistration;
|
||||
|
||||
public SKSizePropertyInputViewModel(LayerProperty<SKSize> layerProperty, IProfileEditorService profileEditorService,
|
||||
IModelValidator<SKSizePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
|
||||
{
|
||||
_widthRegistration = layerProperty.GetDataBindingRegistration<float>("Width");
|
||||
_heightRegistration = layerProperty.GetDataBindingRegistration<float>("Height");
|
||||
}
|
||||
|
||||
// Since SKSize is immutable we need to create properties that replace the SKSize entirely
|
||||
@ -35,20 +30,12 @@ namespace Artemis.UI.DefaultTypes.PropertyInput
|
||||
set => InputValue = new SKSize(Width, value);
|
||||
}
|
||||
|
||||
public bool IsWidthEnabled => _widthRegistration.DataBinding == null;
|
||||
public bool IsHeightEnabled => _heightRegistration.DataBinding == null;
|
||||
|
||||
protected override void OnInputValueChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(Width));
|
||||
NotifyOfPropertyChange(nameof(Height));
|
||||
}
|
||||
|
||||
protected override void OnDataBindingsChanged()
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(IsWidthEnabled));
|
||||
NotifyOfPropertyChange(nameof(IsHeightEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public class SKSizePropertyInputViewModelValidator : AbstractValidator<SKSizePropertyInputViewModel>
|
||||
|
||||
@ -116,11 +116,6 @@ namespace Artemis.UI.Ninject.Factories
|
||||
NodeScriptWindowViewModel NodeScriptWindowViewModel(NodeScript nodeScript);
|
||||
}
|
||||
|
||||
public interface IDataBindingsVmFactory
|
||||
{
|
||||
IDataBindingViewModel DataBindingViewModel(IDataBindingRegistration registration);
|
||||
}
|
||||
|
||||
public interface IPropertyVmFactory
|
||||
{
|
||||
ITreePropertyViewModel TreePropertyViewModel(ILayerProperty layerProperty, LayerPropertyViewModel layerPropertyViewModel);
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings;
|
||||
using Ninject.Extensions.Factory;
|
||||
|
||||
namespace Artemis.UI.Ninject.InstanceProviders
|
||||
{
|
||||
public class DataBindingsViewModelInstanceProvider : StandardInstanceProvider
|
||||
{
|
||||
protected override Type GetType(MethodInfo methodInfo, object[] arguments)
|
||||
{
|
||||
if (methodInfo.ReturnType != typeof(IDataBindingViewModel))
|
||||
return base.GetType(methodInfo, arguments);
|
||||
|
||||
// Find LayerProperty type
|
||||
Type descriptionPropertyType = arguments[0].GetType();
|
||||
while (descriptionPropertyType != null && (!descriptionPropertyType.IsGenericType || descriptionPropertyType.GetGenericTypeDefinition() != typeof(DataBindingRegistration<,>)))
|
||||
descriptionPropertyType = descriptionPropertyType.BaseType;
|
||||
if (descriptionPropertyType == null)
|
||||
return base.GetType(methodInfo, arguments);
|
||||
|
||||
return typeof(DataBindingViewModel<,>).MakeGenericType(descriptionPropertyType.GetGenericArguments());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,6 @@ namespace Artemis.UI.Ninject
|
||||
.BindToFactory();
|
||||
});
|
||||
|
||||
Kernel.Bind<IDataBindingsVmFactory>().ToFactory(() => new DataBindingsViewModelInstanceProvider());
|
||||
Kernel.Bind<IPropertyVmFactory>().ToFactory(() => new LayerPropertyViewModelInstanceProvider());
|
||||
|
||||
// Bind profile editor VMs
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
<UserControl x:Class="Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DataBindingView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/Artemis.UI;component/ResourceDictionaries/DataModelConditions.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="0.50*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<materialDesign:Card Grid.Column="0"
|
||||
UniformCornerRadius="0"
|
||||
materialDesign:ShadowAssist.ShadowDepth="Depth3"
|
||||
materialDesign:ShadowAssist.ShadowEdges="Right"
|
||||
Background="{DynamicResource MaterialDesignPaper}"
|
||||
Panel.ZIndex="2">
|
||||
<Grid Margin="10 5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<CheckBox Grid.Row="0" Margin="0 10" IsChecked="{Binding IsDataBindingEnabled}">Enable data binding</CheckBox>
|
||||
|
||||
<TextBox Grid.Row="1"
|
||||
Style="{StaticResource MaterialDesignFilledTextBox}"
|
||||
Margin="0 5"
|
||||
Text="{Binding EasingTime}"
|
||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||
materialDesign:TextFieldAssist.SuffixText="ms"
|
||||
materialDesign:HintAssist.Hint="Easing time"
|
||||
IsEnabled="{Binding IsDataBindingEnabled}" />
|
||||
|
||||
<ComboBox Grid.Row="2"
|
||||
Style="{StaticResource MaterialDesignFilledComboBox}"
|
||||
Margin="0 5"
|
||||
materialDesign:HintAssist.Hint="Easing type"
|
||||
MinWidth="128"
|
||||
IsEnabled="{Binding IsEasingTimeEnabled}"
|
||||
SelectedItem="{Binding SelectedEasingViewModel}"
|
||||
ItemsSource="{Binding EasingViewModels}">
|
||||
<ComboBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ComboBox.ItemsPanel>
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Polyline Stroke="{DynamicResource MaterialDesignBody}"
|
||||
StrokeThickness="1"
|
||||
Points="{Binding EasingPoints}"
|
||||
Stretch="Uniform"
|
||||
Width="14"
|
||||
Height="14"
|
||||
Margin="0 0 10 0" />
|
||||
<TextBlock Text="{Binding Description}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<materialDesign:Card Grid.Row="3" Grid.ColumnSpan="2" Margin="0 5 0 0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Margin="10 10 0 0">
|
||||
Result
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="0 10 10 0"
|
||||
Visibility="{Binding AlwaysApplyDataBindings.Value, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}}"
|
||||
ToolTip="Check 'Apply data bindings in editor'"
|
||||
Cursor="Help">
|
||||
Other bindings not updating?
|
||||
</TextBlock>
|
||||
<Separator Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Style="{StaticResource MaterialDesignLightSeparator}" Margin="0" />
|
||||
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="10 4 10 10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Margin="0 2" Visibility="{Binding ActiveItem.SupportsTestValue, Converter={x:Static s:BoolToVisibilityConverter.Instance}}">Input</TextBlock>
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Visibility="{Binding ActiveItem.SupportsTestValue, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"
|
||||
s:View.Model="{Binding TestInputValue}"
|
||||
Margin="0 2"
|
||||
FontFamily="Consolas"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Right" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0 2">Output</TextBlock>
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
s:View.Model="{Binding TestResultValue}"
|
||||
Margin="0 2"
|
||||
FontFamily="Consolas"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
|
||||
<Grid Grid.Row="4" VerticalAlignment="Bottom" Margin="0 5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Grid.Column="0"
|
||||
Style="{StaticResource MaterialDesignRaisedAccentButton}"
|
||||
Margin="0 0 5 0"
|
||||
ToolTip="Copy the entire data binding"
|
||||
Command="{s:Action CopyDataBinding}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="ContentCopy" />
|
||||
<TextBlock Text="COPY" Margin="8 0 0 0" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Grid.Column="1"
|
||||
Style="{StaticResource MaterialDesignRaisedAccentButton}"
|
||||
Margin="5 0 0 0"
|
||||
ToolTip="Paste the entire data binding"
|
||||
Command="{s:Action PasteDataBinding}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="ContentPaste" />
|
||||
<TextBlock Text="PASTE" Margin="8 0 0 0" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</materialDesign:Card>
|
||||
|
||||
<materialDesign:Card Grid.Column="1" UniformCornerRadius="0" Background="{DynamicResource MaterialDesignToolBarBackground}" Panel.ZIndex="1">
|
||||
<controls:VisualScriptEditor Script="{Binding Script}" AvailableNodes="{Binding AvailableNodes}"></controls:VisualScriptEditor>
|
||||
</materialDesign:Card>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@ -1,15 +0,0 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DataBindingView.xaml
|
||||
/// </summary>
|
||||
public partial class DataBindingView : UserControl
|
||||
{
|
||||
public DataBindingView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,251 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
{
|
||||
public sealed class DataBindingViewModel<TLayerProperty, TProperty> : Screen, IDataBindingViewModel
|
||||
{
|
||||
private readonly ICoreService _coreService;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private int _easingTime;
|
||||
private bool _isDataBindingEnabled;
|
||||
private bool _isEasingTimeEnabled;
|
||||
private DateTime _lastUpdate;
|
||||
private TimelineEasingViewModel _selectedEasingViewModel;
|
||||
|
||||
private bool _updating;
|
||||
private bool _updatingTestResult;
|
||||
|
||||
public DataBindingViewModel(DataBindingRegistration<TLayerProperty, TProperty> registration,
|
||||
ICoreService coreService,
|
||||
ISettingsService settingsService,
|
||||
IProfileEditorService profileEditorService,
|
||||
IDataModelUIService dataModelUIService,
|
||||
INodeService nodeService)
|
||||
{
|
||||
Registration = registration;
|
||||
_coreService = coreService;
|
||||
_profileEditorService = profileEditorService;
|
||||
|
||||
DisplayName = Registration.DisplayName.ToUpper();
|
||||
AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true);
|
||||
EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
|
||||
AvailableNodes = nodeService.AvailableNodes;
|
||||
TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
|
||||
TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
|
||||
}
|
||||
|
||||
public DataBindingRegistration<TLayerProperty, TProperty> Registration { get; }
|
||||
public NodeScript<TProperty> Script => Registration.DataBinding?.Script;
|
||||
|
||||
public PluginSetting<bool> AlwaysApplyDataBindings { get; }
|
||||
public BindableCollection<TimelineEasingViewModel> EasingViewModels { get; }
|
||||
public IEnumerable<NodeData> AvailableNodes { get; }
|
||||
public DataModelDisplayViewModel TestInputValue { get; }
|
||||
public DataModelDisplayViewModel TestResultValue { get; }
|
||||
|
||||
|
||||
public bool IsDataBindingEnabled
|
||||
{
|
||||
get => _isDataBindingEnabled;
|
||||
set
|
||||
{
|
||||
SetAndNotify(ref _isDataBindingEnabled, value);
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
if (value)
|
||||
EnableDataBinding();
|
||||
else
|
||||
DisableDataBinding();
|
||||
}
|
||||
}
|
||||
|
||||
public TimelineEasingViewModel SelectedEasingViewModel
|
||||
{
|
||||
get => _selectedEasingViewModel;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _selectedEasingViewModel, value)) return;
|
||||
ApplyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public int EasingTime
|
||||
{
|
||||
get => _easingTime;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _easingTime, value)) return;
|
||||
ApplyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEasingTimeEnabled
|
||||
{
|
||||
get => _isEasingTimeEnabled;
|
||||
set
|
||||
{
|
||||
if (!SetAndNotify(ref _isEasingTimeEnabled, value)) return;
|
||||
ApplyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyDataBinding()
|
||||
{
|
||||
if (Registration.DataBinding != null)
|
||||
JsonClipboard.SetObject(Registration.DataBinding.Entity);
|
||||
}
|
||||
|
||||
public void PasteDataBinding()
|
||||
{
|
||||
if (Registration.DataBinding == null)
|
||||
Registration.LayerProperty.EnableDataBinding(Registration);
|
||||
if (Registration.DataBinding == null)
|
||||
throw new ArtemisUIException("Failed to create a data binding in order to paste");
|
||||
|
||||
DataBindingEntity dataBindingEntity = JsonClipboard.GetData<DataBindingEntity>();
|
||||
if (dataBindingEntity == null)
|
||||
return;
|
||||
|
||||
Registration.DataBinding.EasingTime = dataBindingEntity.EasingTime;
|
||||
Registration.DataBinding.EasingFunction = (Easings.Functions) dataBindingEntity.EasingFunction;
|
||||
|
||||
// TODO - Paste visual script
|
||||
|
||||
Update();
|
||||
|
||||
_profileEditorService.SaveSelectedProfileElement();
|
||||
}
|
||||
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
Initialize();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast<Easings.Functions>().Select(v => new TimelineEasingViewModel(v, false)));
|
||||
|
||||
_lastUpdate = DateTime.Now;
|
||||
_coreService.FrameRendered += OnFrameRendered;
|
||||
Update();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
if (Registration.DataBinding == null)
|
||||
{
|
||||
IsDataBindingEnabled = false;
|
||||
IsEasingTimeEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_updating = true;
|
||||
|
||||
EasingTime = (int) Registration.DataBinding.EasingTime.TotalMilliseconds;
|
||||
SelectedEasingViewModel = EasingViewModels.First(vm => vm.EasingFunction == Registration.DataBinding.EasingFunction);
|
||||
IsDataBindingEnabled = true;
|
||||
IsEasingTimeEnabled = EasingTime > 0;
|
||||
|
||||
_updating = false;
|
||||
}
|
||||
|
||||
private void ApplyChanges()
|
||||
{
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
if (Registration.DataBinding != null)
|
||||
{
|
||||
Registration.DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime);
|
||||
Registration.DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear;
|
||||
}
|
||||
|
||||
_profileEditorService.SaveSelectedProfileElement();
|
||||
Update();
|
||||
}
|
||||
|
||||
private void UpdateTestResult()
|
||||
{
|
||||
if (_updating || _updatingTestResult)
|
||||
return;
|
||||
|
||||
_updatingTestResult = true;
|
||||
|
||||
if (Registration.DataBinding == null)
|
||||
{
|
||||
TestInputValue.UpdateValue(default);
|
||||
TestResultValue.UpdateValue(default);
|
||||
_updatingTestResult = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If always update is disabled, do constantly update the data binding as long as the view model is open
|
||||
// If always update is enabled, this is done for all data bindings in ProfileViewModel
|
||||
if (!AlwaysApplyDataBindings.Value)
|
||||
{
|
||||
Registration.DataBinding.UpdateWithDelta(DateTime.Now - _lastUpdate);
|
||||
_profileEditorService.UpdateProfilePreview();
|
||||
}
|
||||
|
||||
TProperty currentValue = Registration.Converter.ConvertFromObject(Registration.DataBinding.Script.Result ?? default(TProperty));
|
||||
TestInputValue.UpdateValue(currentValue);
|
||||
TestResultValue.UpdateValue(Registration.DataBinding != null ? Registration.DataBinding.GetValue(currentValue) : default);
|
||||
|
||||
_updatingTestResult = false;
|
||||
}
|
||||
|
||||
private void EnableDataBinding()
|
||||
{
|
||||
if (Registration.DataBinding != null)
|
||||
return;
|
||||
|
||||
Registration.LayerProperty.EnableDataBinding(Registration);
|
||||
NotifyOfPropertyChange(nameof(Script));
|
||||
|
||||
_profileEditorService.SaveSelectedProfileElement();
|
||||
}
|
||||
|
||||
private void DisableDataBinding()
|
||||
{
|
||||
if (Registration.DataBinding == null)
|
||||
return;
|
||||
|
||||
Registration.LayerProperty.DisableDataBinding(Registration.DataBinding);
|
||||
NotifyOfPropertyChange(nameof(Script));
|
||||
Update();
|
||||
|
||||
_profileEditorService.SaveSelectedProfileElement();
|
||||
}
|
||||
|
||||
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
UpdateTestResult();
|
||||
_lastUpdate = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_coreService.FrameRendered -= OnFrameRendered;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDataBindingViewModel : IScreen, IDisposable
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -4,20 +4,48 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:s="https://github.com/canton7/Stylet"
|
||||
xmlns:controls="clr-namespace:Artemis.VisualScripting.Editor.Controls;assembly=Artemis.VisualScripting"
|
||||
xmlns:layerProperties="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<TabControl ItemsSource="{Binding Items}"
|
||||
SelectedIndex="{Binding SelectedItemIndex}"
|
||||
Style="{StaticResource MaterialDesignTabControl}" >
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl s:View.Model="{Binding}" TextElement.Foreground="{DynamicResource MaterialDesignBody}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Margin="-12 0" Text="{Binding DisplayName}"></TextBlock>
|
||||
</DataTemplate>
|
||||
</TabControl.ItemTemplate>
|
||||
</TabControl>
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance {x:Type layerProperties:DataBindingsViewModel}}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline6TextBlock}"
|
||||
VerticalAlignment="Center"
|
||||
Margin="15 0"
|
||||
Text="{Binding DataBinding.BaseLayerProperty.PropertyDescription.Name}" />
|
||||
<CheckBox Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding DataBindingEnabled}">
|
||||
Enable data binding
|
||||
</CheckBox>
|
||||
</Grid>
|
||||
|
||||
<controls:VisualScriptPresenter Grid.Row="1" AvailableNodes="{Binding AvailableNodes}" Script="{Binding DataBinding.Script}" />
|
||||
<Border Grid.Row="1" Background="{StaticResource MaterialDesignToolBarBackground}"
|
||||
Visibility="{Binding DataBindingEnabled, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="16">
|
||||
<materialDesign:PackIcon Kind="TransitConnectionVariant" Width="100" Height="100" HorizontalAlignment="Center" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline4TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="0 25">
|
||||
Enable this data binding to use nodes!
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" TextWrapping="Wrap" HorizontalAlignment="Center" TextAlignment="Center">
|
||||
When you enable data bindings you can no longer use keyframes or normal values for this property. <LineBreak />
|
||||
Instead you'll be using our node scripting system to set a value based on the data model or values of other layers/folders.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@ -1,88 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Ninject.Factories;
|
||||
using Artemis.Core.Services;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
{
|
||||
public class DataBindingsViewModel : Conductor<IDataBindingViewModel>.Collection.AllActive
|
||||
public class DataBindingsViewModel : Screen
|
||||
{
|
||||
private readonly IDataBindingsVmFactory _dataBindingsVmFactory;
|
||||
private readonly IProfileEditorService _profileEditorService;
|
||||
private ILayerProperty _selectedDataBinding;
|
||||
private int _selectedItemIndex;
|
||||
private bool _updating;
|
||||
private IDataBinding _dataBinding;
|
||||
|
||||
public DataBindingsViewModel(IProfileEditorService profileEditorService, IDataBindingsVmFactory dataBindingsVmFactory)
|
||||
public DataBindingsViewModel(IProfileEditorService profileEditorService, INodeService nodeService)
|
||||
{
|
||||
_profileEditorService = profileEditorService;
|
||||
_dataBindingsVmFactory = dataBindingsVmFactory;
|
||||
AvailableNodes = nodeService.AvailableNodes.ToList();
|
||||
}
|
||||
|
||||
public int SelectedItemIndex
|
||||
public List<NodeData> AvailableNodes { get; }
|
||||
|
||||
public IDataBinding DataBinding
|
||||
{
|
||||
get => _selectedItemIndex;
|
||||
set => SetAndNotify(ref _selectedItemIndex, value);
|
||||
get => _dataBinding;
|
||||
set => SetAndNotify(ref _dataBinding, value);
|
||||
}
|
||||
|
||||
private void CreateDataBindingViewModels()
|
||||
public bool DataBindingEnabled
|
||||
{
|
||||
int oldIndex = SelectedItemIndex;
|
||||
Items.Clear();
|
||||
|
||||
ILayerProperty layerProperty = _profileEditorService.SelectedDataBinding;
|
||||
if (layerProperty == null)
|
||||
return;
|
||||
|
||||
List<IDataBindingRegistration> registrations = layerProperty.GetAllDataBindingRegistrations();
|
||||
|
||||
// Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving
|
||||
// and creating the actual data bindings
|
||||
Items.AddRange(registrations.Select(registration => _dataBindingsVmFactory.DataBindingViewModel(registration)));
|
||||
|
||||
SelectedItemIndex = Items.Count < oldIndex ? 0 : oldIndex;
|
||||
get => _dataBinding?.IsEnabled ?? false;
|
||||
set
|
||||
{
|
||||
if (_dataBinding != null)
|
||||
_dataBinding.IsEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
|
||||
{
|
||||
CreateDataBindingViewModels();
|
||||
SubscribeToSelectedDataBinding();
|
||||
|
||||
SelectedItemIndex = 0;
|
||||
}
|
||||
|
||||
private void SubscribeToSelectedDataBinding()
|
||||
{
|
||||
if (_selectedDataBinding != null)
|
||||
if (DataBinding != null)
|
||||
{
|
||||
_selectedDataBinding.DataBindingPropertyRegistered -= DataBindingRegistrationsChanged;
|
||||
_selectedDataBinding.DataBindingPropertiesCleared -= DataBindingRegistrationsChanged;
|
||||
DataBinding.DataBindingEnabled -= DataBindingOnDataBindingToggled;
|
||||
DataBinding.DataBindingDisabled -= DataBindingOnDataBindingToggled;
|
||||
}
|
||||
|
||||
_selectedDataBinding = _profileEditorService.SelectedDataBinding;
|
||||
if (_selectedDataBinding != null)
|
||||
DataBinding = _profileEditorService.SelectedDataBinding;
|
||||
if (DataBinding != null)
|
||||
{
|
||||
_selectedDataBinding.DataBindingPropertyRegistered += DataBindingRegistrationsChanged;
|
||||
_selectedDataBinding.DataBindingPropertiesCleared += DataBindingRegistrationsChanged;
|
||||
DataBinding.DataBindingEnabled += DataBindingOnDataBindingToggled;
|
||||
DataBinding.DataBindingDisabled += DataBindingOnDataBindingToggled;
|
||||
|
||||
OnPropertyChanged(nameof(DataBindingEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
private void DataBindingRegistrationsChanged(object sender, LayerPropertyEventArgs e)
|
||||
private void DataBindingOnDataBindingToggled(object? sender, DataBindingEventArgs e)
|
||||
{
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
_updating = true;
|
||||
Execute.PostToUIThread(async () =>
|
||||
{
|
||||
await Task.Delay(200);
|
||||
CreateDataBindingViewModels();
|
||||
_updating = false;
|
||||
});
|
||||
OnPropertyChanged(nameof(DataBindingEnabled));
|
||||
}
|
||||
|
||||
#region Overrides of Screen
|
||||
@ -90,17 +70,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
||||
protected override void OnInitialActivate()
|
||||
{
|
||||
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
CreateDataBindingViewModels();
|
||||
SubscribeToSelectedDataBinding();
|
||||
base.OnInitialActivate();
|
||||
}
|
||||
|
||||
protected override void OnActivate()
|
||||
{
|
||||
SelectedItemIndex = 0;
|
||||
base.OnActivate();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
_profileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
|
||||
@ -156,7 +156,6 @@
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
<!-- Properties tree -->
|
||||
<materialDesign:DialogHost Identifier="PropertyTreeDialogHost" DialogTheme="Inherit" CloseOnClickAway="True" Grid.Row="1">
|
||||
<materialDesign:Transitioner x:Name="PropertyTreeTransitioner" SelectedIndex="{Binding PropertyTreeIndex}" DefaultTransitionOrigin="0.9, 0.9" AutoApplyTransitionOrigins="True">
|
||||
|
||||
@ -39,10 +39,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
|
||||
public void ActivateDataBindingViewModel()
|
||||
{
|
||||
if (_profileEditorService.SelectedDataBinding == LayerProperty)
|
||||
if (_profileEditorService.SelectedDataBinding == LayerProperty.DataBinding)
|
||||
_profileEditorService.ChangeSelectedDataBinding(null);
|
||||
else
|
||||
_profileEditorService.ChangeSelectedDataBinding(LayerProperty);
|
||||
_profileEditorService.ChangeSelectedDataBinding(LayerProperty.DataBinding);
|
||||
}
|
||||
|
||||
public void ResetToDefault()
|
||||
@ -92,8 +92,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
_profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
LayerProperty.VisibilityChanged += LayerPropertyOnVisibilityChanged;
|
||||
LayerProperty.DataBindingEnabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBindingDisabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingEnabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingDisabled += LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.KeyframesToggled += LayerPropertyOnKeyframesToggled;
|
||||
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
|
||||
base.OnInitialActivate();
|
||||
@ -104,8 +104,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
{
|
||||
_profileEditorService.SelectedDataBindingChanged -= ProfileEditorServiceOnSelectedDataBindingChanged;
|
||||
LayerProperty.VisibilityChanged -= LayerPropertyOnVisibilityChanged;
|
||||
LayerProperty.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingEnabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.DataBinding.DataBindingDisabled -= LayerPropertyOnDataBindingChange;
|
||||
LayerProperty.KeyframesToggled -= LayerPropertyOnKeyframesToggled;
|
||||
base.OnClose();
|
||||
}
|
||||
@ -116,7 +116,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
|
||||
private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e)
|
||||
{
|
||||
LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty;
|
||||
LayerPropertyViewModel.IsHighlighted = _profileEditorService.SelectedDataBinding == LayerProperty.DataBinding;
|
||||
}
|
||||
|
||||
private void LayerPropertyOnVisibilityChanged(object sender, LayerPropertyEventArgs e)
|
||||
@ -124,7 +124,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree
|
||||
LayerPropertyViewModel.IsVisible = !LayerProperty.IsHidden;
|
||||
}
|
||||
|
||||
private void LayerPropertyOnDataBindingChange(object sender, LayerPropertyEventArgs e)
|
||||
private void LayerPropertyOnDataBindingChange(object sender, DataBindingEventArgs e)
|
||||
{
|
||||
NotifyOfPropertyChange(nameof(HasDataBinding));
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
private bool _canSelectEditTool;
|
||||
private BindableCollection<ArtemisDevice> _devices;
|
||||
private BindableCollection<ArtemisLed> _highlightedLeds;
|
||||
private DateTime _lastUpdate;
|
||||
private PanZoomViewModel _panZoomViewModel;
|
||||
private Layer _previousSelectedLayer;
|
||||
private int _previousTool;
|
||||
@ -138,7 +137,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
|
||||
ApplyActiveProfile();
|
||||
|
||||
_lastUpdate = DateTime.Now;
|
||||
_coreService.FrameRendered += OnFrameRendered;
|
||||
|
||||
_rgbService.DeviceAdded += RgbServiceOnDevicesModified;
|
||||
@ -232,7 +230,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
if (CanSelectEditTool == false && ActiveToolIndex == 1)
|
||||
ActivateToolByIndex(2);
|
||||
}
|
||||
|
||||
|
||||
#region Buttons
|
||||
|
||||
private void ActivateToolByIndex(int value)
|
||||
@ -307,19 +305,32 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization
|
||||
|
||||
private void OnFrameRendered(object sender, FrameRenderedEventArgs e)
|
||||
{
|
||||
TimeSpan delta = DateTime.Now - _lastUpdate;
|
||||
_lastUpdate = DateTime.Now;
|
||||
|
||||
if (SuspendedEditing || !_settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true).Value || _profileEditorService.SelectedProfile == null)
|
||||
if (SuspendedEditing || _profileEditorService.Playing || _profileEditorService.SelectedProfile == null)
|
||||
return;
|
||||
|
||||
foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllRenderElements()
|
||||
.SelectMany(f => f.GetAllLayerProperties(), (f, p) => p)
|
||||
.SelectMany(p => p.GetAllDataBindingRegistrations()))
|
||||
dataBindingRegistration.GetDataBinding()?.UpdateWithDelta(delta);
|
||||
|
||||
// TODO: Only update when there are data bindings
|
||||
if (!_profileEditorService.Playing)
|
||||
bool hasEnabledDataBindings = false;
|
||||
|
||||
// Always update the currently selected data binding
|
||||
if (_profileEditorService.SelectedDataBinding != null && _profileEditorService.SelectedDataBinding.IsEnabled)
|
||||
{
|
||||
hasEnabledDataBindings = true;
|
||||
_profileEditorService.SelectedDataBinding.BaseLayerProperty.UpdateDataBinding();
|
||||
}
|
||||
|
||||
// Update all other data bindings if the user enabled this
|
||||
if (_settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true).Value)
|
||||
{
|
||||
foreach (ILayerProperty layerProperty in _profileEditorService.SelectedProfile.GetAllRenderElements().SelectMany(f => f.GetAllLayerProperties(), (_, p) => p))
|
||||
{
|
||||
if (layerProperty.BaseDataBinding != _profileEditorService.SelectedDataBinding && layerProperty.BaseDataBinding.IsEnabled)
|
||||
{
|
||||
hasEnabledDataBindings = true;
|
||||
layerProperty.UpdateDataBinding();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEnabledDataBindings)
|
||||
_profileEditorService.UpdateProfilePreview();
|
||||
}
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||
if (_node.ProfileElement == null)
|
||||
return;
|
||||
|
||||
LayerProperties.AddRange(_node.ProfileElement.GetAllLayerProperties().Where(l => l.GetAllDataBindingRegistrations().Any()));
|
||||
LayerProperties.AddRange(_node.ProfileElement.GetAllLayerProperties().Where(l => l.DataBindingsSupported));
|
||||
_selectedLayerProperty = _node.LayerProperty;
|
||||
NotifyOfPropertyChange(nameof(SelectedLayerProperty));
|
||||
}
|
||||
|
||||
@ -26,16 +26,16 @@ namespace Artemis.VisualScripting.Nodes
|
||||
return;
|
||||
}
|
||||
|
||||
List<IDataBindingRegistration> list = LayerProperty.GetAllDataBindingRegistrations();
|
||||
List<IDataBindingProperty> list = LayerProperty.BaseDataBinding.Properties.ToList();
|
||||
int index = 0;
|
||||
foreach (IPin pin in Pins)
|
||||
{
|
||||
OutputPin outputPin = (OutputPin) pin;
|
||||
IDataBindingRegistration dataBindingRegistration = list[index];
|
||||
IDataBindingProperty dataBindingProperty = list[index];
|
||||
index++;
|
||||
|
||||
// TODO: Is this really non-nullable?
|
||||
outputPin.Value = dataBindingRegistration.GetValue();
|
||||
outputPin.Value = dataBindingProperty.GetValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,7 +108,7 @@ namespace Artemis.VisualScripting.Nodes
|
||||
if (LayerProperty == null)
|
||||
return;
|
||||
|
||||
foreach (IDataBindingRegistration dataBindingRegistration in LayerProperty.GetAllDataBindingRegistrations())
|
||||
foreach (IDataBindingProperty dataBindingRegistration in LayerProperty.BaseDataBinding.Properties)
|
||||
CreateOutputPin(dataBindingRegistration.ValueType, dataBindingRegistration.DisplayName);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user