diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 6e416d8dd..4cb259807 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -84,4 +84,8 @@ PreserveNewest + + + + \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs deleted file mode 100644 index 75818ac95..000000000 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -namespace Artemis.Core -{ - /// - public class FloatDataBindingConverter : FloatDataBindingConverter - { - } - - /// - /// The type of layer property this converter is applied to - public class FloatDataBindingConverter : DataBindingConverter - { - /// - /// Creates a new instance of the class - /// - public FloatDataBindingConverter() - { - SupportsSum = true; - SupportsInterpolate = true; - } - - /// - public override float Sum(float a, float b) - { - return a + b; - } - - /// - public override float Interpolate(float a, float b, double progress) - { - float diff = b - a; - return (float) (a + diff * progress); - } - - /// - 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); - } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/GeneralDataBindingConverter.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Converters/GeneralDataBindingConverter.cs deleted file mode 100644 index 584967a76..000000000 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/GeneralDataBindingConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace Artemis.Core -{ - /// - /// Represents a generic data binding converter that acts as the bridge between a - /// and a and does not support - /// sum or interpolation - /// - public class GeneralDataBindingConverter : DataBindingConverter - { - /// - /// Creates a new instance of the class - /// - public GeneralDataBindingConverter() - { - SupportsSum = false; - SupportsInterpolate = false; - } - - /// - public override T Sum(T a, T b) - { - throw new NotSupportedException(); - } - - /// - public override T Interpolate(T a, T b, double progress) - { - throw new NotSupportedException(); - } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/IntDataBindingConverter.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Converters/IntDataBindingConverter.cs deleted file mode 100644 index 795145630..000000000 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/IntDataBindingConverter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; - -namespace Artemis.Core -{ - /// - public class IntDataBindingConverter : IntDataBindingConverter - { - } - - /// - public class IntDataBindingConverter : DataBindingConverter - { - /// - /// Creates a new instance of the class - /// - public IntDataBindingConverter() - { - SupportsSum = true; - SupportsInterpolate = true; - } - - /// - /// Gets or sets the mode used for rounding during interpolation. Defaults to - /// - /// - public MidpointRounding InterpolationRoundingMode { get; set; } = MidpointRounding.AwayFromZero; - - /// - public override int Sum(int a, int b) - { - return a + b; - } - - /// - public override int Interpolate(int a, int b, double progress) - { - int diff = b - a; - return (int) Math.Round(a + diff * progress, InterpolationRoundingMode); - } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/SKColorDataBindingConverter.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Converters/SKColorDataBindingConverter.cs deleted file mode 100644 index 6b708e088..000000000 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/SKColorDataBindingConverter.cs +++ /dev/null @@ -1,29 +0,0 @@ -using SkiaSharp; - -namespace Artemis.Core -{ - /// - public class SKColorDataBindingConverter : DataBindingConverter - { - /// - /// Creates a new instance of the class - /// - public SKColorDataBindingConverter() - { - SupportsInterpolate = true; - SupportsSum = true; - } - - /// - public override SKColor Sum(SKColor a, SKColor b) - { - return a.Sum(b); - } - - /// - public override SKColor Interpolate(SKColor a, SKColor b, double progress) - { - return a.Interpolate(b, (float) progress); - } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs index 99a7b614a..ccaecf8b8 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs @@ -4,9 +4,14 @@ public class BoolLayerProperty : LayerProperty { internal BoolLayerProperty() + { + } + + /// + protected override void OnInitialize() { KeyframesSupported = false; - RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new GeneralDataBindingConverter(), "Value"); + DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs index e811a4e0f..75d7c8a61 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs @@ -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 - { - public ColorStopDataBindingConverter() - { - SupportsInterpolate = true; - SupportsSum = true; - } - - /// - public override SKColor Sum(SKColor a, SKColor b) - { - return a.Sum(b); - } - - /// - public override SKColor Interpolate(SKColor a, SKColor b, double progress) - { - return a.Interpolate(b, (float) progress); - } - } } \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Properties/EnumLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/EnumLayerProperty.cs index eb99a3b3e..ea7962b7a 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/EnumLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/EnumLayerProperty.cs @@ -8,7 +8,6 @@ namespace Artemis.Core internal EnumLayerProperty() { KeyframesSupported = false; - DataBindingsSupported = false; } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs index cb1a6eae8..2aec47126 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs @@ -5,7 +5,12 @@ { internal FloatLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new FloatDataBindingConverter(), "Value"); + } + + /// + protected override void OnInitialize() + { + DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs index 34a3be646..d43dbf705 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs @@ -5,13 +5,17 @@ { internal FloatRangeLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new FloatDataBindingConverter(), "Start"); - RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new FloatDataBindingConverter(), "End"); - CurrentValueSet += OnCurrentValueSet; DefaultValue = new FloatRange(); } + /// + protected override void OnInitialize() + { + DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, "Start"); + DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, "End"); + } + /// protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased) { diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs index f69a1bdbc..03681df72 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs @@ -7,7 +7,12 @@ namespace Artemis.Core { internal IntLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new IntDataBindingConverter(), "Value"); + } + + /// + protected override void OnInitialize() + { + DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs index 0b1942356..fea761b8e 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs @@ -5,13 +5,17 @@ { internal IntRangeLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new IntDataBindingConverter(), "Start"); - RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new IntDataBindingConverter(), "End"); - CurrentValueSet += OnCurrentValueSet; DefaultValue = new IntRange(); } + /// + protected override void OnInitialize() + { + DataBinding.RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, "Start"); + DataBinding.RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, "End"); + } + /// protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased) { diff --git a/src/Artemis.Core/DefaultTypes/Properties/LayerBrushReferenceLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/LayerBrushReferenceLayerProperty.cs index 9bbd77eac..c3341ee32 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/LayerBrushReferenceLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/LayerBrushReferenceLayerProperty.cs @@ -8,7 +8,6 @@ internal LayerBrushReferenceLayerProperty() { KeyframesSupported = false; - DataBindingsSupported = false; } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs index 46370568a..38caad133 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs @@ -7,7 +7,12 @@ namespace Artemis.Core { internal SKColorLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new SKColorDataBindingConverter(), "Value"); + } + + /// + protected override void OnInitialize() + { + DataBinding.RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs index 3cd654ad9..df9517eeb 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs @@ -7,8 +7,13 @@ namespace Artemis.Core { internal SKPointLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue.X, value => CurrentValue = new SKPoint(value, CurrentValue.Y), new FloatDataBindingConverter(), "X"); - RegisterDataBindingProperty(() => CurrentValue.Y, value => CurrentValue = new SKPoint(CurrentValue.X, value), new FloatDataBindingConverter(), "Y"); + } + + /// + 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"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs index 3274c26f9..e8344e0c9 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs @@ -7,8 +7,13 @@ namespace Artemis.Core { internal SKSizeLayerProperty() { - RegisterDataBindingProperty(() => CurrentValue.Width, (value) => CurrentValue = new SKSize(value, CurrentValue.Height), new FloatDataBindingConverter(), "Width"); - RegisterDataBindingProperty(() => CurrentValue.Height, (value) => CurrentValue = new SKSize(CurrentValue.Width, value), new FloatDataBindingConverter(), "Height"); + } + + /// + 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"); } /// diff --git a/src/Artemis.Core/Events/Profiles/DataBindingEventArgs.cs b/src/Artemis.Core/Events/Profiles/DataBindingEventArgs.cs new file mode 100644 index 000000000..c01cfae65 --- /dev/null +++ b/src/Artemis.Core/Events/Profiles/DataBindingEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace Artemis.Core +{ + /// + /// Provides data for data binding events. + /// + public class DataBindingEventArgs : EventArgs + { + internal DataBindingEventArgs(IDataBinding dataBinding) + { + DataBinding = dataBinding; + } + + /// + /// Gets the data binding this event is related to + /// + public IDataBinding DataBinding { get; } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index e2392c79e..e02908256 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -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 { /// - public class DataBinding : IDataBinding + public class DataBinding : IDataBinding { - private TProperty _currentValue = default!; + private readonly List _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 dataBindingRegistration) + internal DataBinding(LayerProperty layerProperty) { - LayerProperty = dataBindingRegistration.LayerProperty; - Entity = new DataBindingEntity(); + LayerProperty = layerProperty; - ApplyRegistration(dataBindingRegistration); - Script = new NodeScript(GetScriptName(), "The value to put into the data binding", LayerProperty.ProfileElement.Profile); + Entity = new DataBindingEntity(); + Script = new DataBindingNodeScript(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile); Save(); } @@ -27,39 +26,23 @@ namespace Artemis.Core internal DataBinding(LayerProperty layerProperty, DataBindingEntity entity) { LayerProperty = layerProperty; + Entity = entity; - Script = new NodeScript(GetScriptName(), "The value to put into the data binding", LayerProperty.ProfileElement.Profile); + Script = new DataBindingNodeScript(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile); // Load will add children so be initialized before that Load(); } - /// - /// Gets the data binding registration this data binding is based upon - /// - public DataBindingRegistration? Registration { get; private set; } - /// /// Gets the layer property this data binding targets /// public LayerProperty LayerProperty { get; } /// - /// Gets the converter used to apply this data binding to the + /// Gets the script used to populate the data binding /// - public DataBindingConverter? Converter { get; private set; } - - public NodeScript Script { get; private set; } - - /// - /// Gets or sets the easing time of the data binding - /// - public TimeSpan EasingTime { get; set; } - - /// - /// Gets ors ets the easing function of the data binding - /// - public Easings.Functions EasingFunction { get; set; } + public DataBindingNodeScript Script { get; private set; } /// /// Gets the data binding entity this data binding uses for persistent storage @@ -67,38 +50,52 @@ namespace Artemis.Core public DataBindingEntity Entity { get; } /// - /// Gets the current value of the data binding + /// Updates the pending values of this data binding /// - /// The base value of the property the data binding is applied to - /// - 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(); } /// - /// 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 /// - public Type? GetTargetType() + /// The type of the layer property + /// The function to call to get the value of the property + /// The action to call to set the value of the property + /// The display name of the data binding property + public DataBindingProperty RegisterDataBindingProperty(Func getter, Action 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 property = new(getter, setter, displayName); + _properties.Add(property); + + OnDataBindingPropertyRegistered(); + return property; + } + + /// + /// Removes all data binding properties so they are no longer available to the data binding system + /// + public void ClearDataBindingProperties() + { + if (_disposed) + throw new ObjectDisposedException("LayerProperty"); + + _properties.Clear(); + OnDataBindingPropertiesCleared(); } /// @@ -113,111 +110,78 @@ namespace Artemis.Core if (disposing) { _disposed = true; - - if (Registration != null) - Registration.DataBinding = null; - Script.Dispose(); } } - private void ResetEasing(TProperty value) + /// + /// Invokes the event + /// + protected virtual void OnDataBindingPropertyRegistered() { - _previousValue = GetInterpolatedValue(); - _currentValue = value; - _easingProgress = TimeSpan.Zero; - } - - private void ApplyRegistration(DataBindingRegistration 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)); } /// - /// Updates the smoothing progress of the data binding + /// Invokes the event /// - /// The timeline to apply during update - 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); + /// + /// Invokes the event + /// + protected virtual void OnDataBindingEnabled(DataBindingEventArgs e) + { + DataBindingEnabled?.Invoke(this, e); + } + + /// + /// Invokes the event + /// + protected virtual void OnDataBindingDisabled(DataBindingEventArgs e) + { + DataBindingDisabled?.Invoke(this, e); + } + + private string GetScriptName() + { + return LayerProperty.PropertyDescription.Name ?? LayerProperty.Path; } /// - public void UpdateWithDelta(TimeSpan delta) + public ILayerProperty BaseLayerProperty => LayerProperty; + + /// + 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)); + } } + /// + public ReadOnlyCollection Properties => _properties.AsReadOnly(); + /// 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(); } /// @@ -227,6 +191,19 @@ namespace Artemis.Core GC.SuppressFinalize(this); } + /// + public event EventHandler? DataBindingPropertyRegistered; + + /// + public event EventHandler? DataBindingPropertiesCleared; + + /// + public event EventHandler? DataBindingEnabled; + + /// + public event EventHandler? DataBindingDisabled; + + #region Storage /// @@ -234,14 +211,6 @@ namespace Artemis.Core { if (_disposed) throw new ObjectDisposedException("DataBinding"); - - // General - DataBindingRegistration? registration = LayerProperty.GetDataBindingRegistration(Entity.Identifier); - if (registration != null) - ApplyRegistration(registration); - - EasingTime = Entity.EasingTime; - EasingFunction = (Easings.Functions) Entity.EasingFunction; } /// @@ -249,8 +218,8 @@ namespace Artemis.Core { Script.Dispose(); Script = Entity.NodeScript != null - ? new NodeScript(GetScriptName(), "The value to put into the data binding", Entity.NodeScript, LayerProperty.ProfileElement.Profile) - : new NodeScript(GetScriptName(), "The value to put into the data binding", LayerProperty.ProfileElement.Profile); + ? new DataBindingNodeScript(GetScriptName(), "The value to put into the data binding", this, Entity.NodeScript, LayerProperty.ProfileElement.Profile) + : new DataBindingNodeScript(GetScriptName(), "The value to put into the data binding", this, LayerProperty.ProfileElement.Profile); } /// @@ -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; } diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs deleted file mode 100644 index 2b3d7eb5a..000000000 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; - -namespace Artemis.Core -{ - /// - /// Represents a data binding converter that acts as the bridge between a - /// and a - /// - public abstract class DataBindingConverter : IDataBindingConverter - { - /// - /// Gets the data binding this converter is applied to - /// - public DataBinding? DataBinding { get; private set; } - - /// - /// Gets whether or not this data binding converter supports the method - /// - public bool SupportsSum { get; protected set; } - - /// - /// Gets whether or not this data binding converter supports the method - /// - public bool SupportsInterpolate { get; protected set; } - - /// - /// Returns the sum of and - /// - public abstract TProperty Sum(TProperty a, TProperty b); - - /// - /// Returns the the interpolated value between and on a scale (generally) - /// between 0.0 and 1.0 defined by the - /// Note: The progress may go be negative or go beyond 1.0 depending on the easing method used - /// - /// The value to interpolate away from - /// The value to interpolate towards - /// The progress of the interpolation between 0.0 and 1.0 - /// - public abstract TProperty Interpolate(TProperty a, TProperty b, double progress); - - /// - /// Applies the to the layer property - /// - /// - public virtual void ApplyValue(TProperty value) - { - if (DataBinding?.Registration == null) - throw new ArtemisCoreException("Data binding converter is not yet initialized"); - DataBinding.Registration.Setter(value); - } - - /// - /// Returns the current base value of the data binding - /// - public virtual TProperty GetValue() - { - if (DataBinding?.Registration == null) - throw new ArtemisCoreException("Data binding converter is not yet initialized"); - return DataBinding.Registration.Getter(); - } - - /// - /// Converts the provided object to a type of - /// - public virtual TProperty ConvertFromObject(object? source) - { - return (TProperty) Convert.ChangeType(source, typeof(TProperty))!; - } - - /// - /// Called when the data binding converter has been initialized and the is available - /// - protected virtual void OnInitialized() - { - } - - internal void Initialize(DataBinding dataBinding) - { - if (dataBinding.Registration == null) - throw new ArtemisCoreException("Cannot initialize a data binding converter for a data binding without a registration"); - - DataBinding = dataBinding; - OnInitialized(); - } - - /// - public Type SupportedType => typeof(TProperty); - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingProperty.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingProperty.cs new file mode 100644 index 000000000..6c67300a2 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingProperty.cs @@ -0,0 +1,46 @@ +using System; + +namespace Artemis.Core +{ + /// + public class DataBindingProperty : IDataBindingProperty + { + internal DataBindingProperty(Func getter, Action setter, string displayName) + { + Getter = getter ?? throw new ArgumentNullException(nameof(getter)); + Setter = setter ?? throw new ArgumentNullException(nameof(setter)); + DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName)); + } + + /// + /// Gets the function to call to get the value of the property + /// + public Func Getter { get; } + + /// + /// Gets the action to call to set the value of the property + /// + public Action Setter { get; } + + /// + public string DisplayName { get; } + + /// + public Type ValueType => typeof(TProperty); + + /// + public object? GetValue() + { + return Getter(); + } + + /// + 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)); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs deleted file mode 100644 index e7d5ca1d1..000000000 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Linq; -using Artemis.Storage.Entities.Profile.DataBindings; - -namespace Artemis.Core -{ - /// - public class DataBindingRegistration : IDataBindingRegistration - { - internal DataBindingRegistration(LayerProperty layerProperty, DataBindingConverter converter, - Func getter, Action 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)); - } - - /// - /// Gets the layer property this registration was made on - /// - public LayerProperty LayerProperty { get; } - - /// - /// Gets the converter that's used by the data binding - /// - public DataBindingConverter Converter { get; } - - /// - /// Gets the function to call to get the value of the property - /// - public Func Getter { get; } - - /// - /// Gets the action to call to set the value of the property - /// - public Action Setter { get; } - - /// - public string DisplayName { get; } - - /// - public Type ValueType => typeof(TProperty); - - /// - /// Gets the data binding created using this registration - /// - public DataBinding? DataBinding { get; internal set; } - - /// - public IDataBinding? GetDataBinding() - { - return DataBinding; - } - - /// - 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(LayerProperty, dataBinding); - return DataBinding; - } - - /// - 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); - } - - /// - public object? GetValue() - { - return Getter(); - } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs index 3c7c995bd..e706a3701 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/IDataBinding.cs @@ -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 to a value inside a /// /// - public interface IDataBinding : IStorageModel, IUpdateModel, IDisposable + public interface IDataBinding : IStorageModel, IDisposable { /// - /// Updates the smoothing progress of the data binding and recalculates the value next call + /// Gets the layer property the data binding is applied to /// - /// The delta to apply during update - void UpdateWithDelta(TimeSpan delta); + ILayerProperty BaseLayerProperty { get; } /// - /// Applies the data binding to the layer property + /// Gets a list of sub-properties this data binding applies to + /// + ReadOnlyCollection Properties { get; } + + /// + /// Gets a boolean indicating whether the data binding is enabled or not + /// + bool IsEnabled { get; set; } + + /// + /// Applies the pending value of the data binding to the property /// void Apply(); - + /// /// If the data binding is enabled, loads the node script for that data binding /// void LoadNodeScript(); + + /// + /// Occurs when a data binding property has been added + /// + public event EventHandler? DataBindingPropertyRegistered; + + /// + /// Occurs when all data binding properties have been removed + /// + public event EventHandler? DataBindingPropertiesCleared; + + /// + /// Occurs when a data binding has been enabled + /// + public event EventHandler? DataBindingEnabled; + + /// + /// Occurs when a data binding has been disabled + /// + public event EventHandler? DataBindingDisabled; } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingConverter.cs deleted file mode 100644 index 9f207133f..000000000 --- a/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingConverter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace Artemis.Core -{ - /// - /// Represents a data binding converter that acts as the bridge between a - /// and a - /// - public interface IDataBindingConverter - { - /// - /// Gets the type this converter supports - /// - public Type SupportedType { get; } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs b/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingProperty.cs similarity index 55% rename from src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs rename to src/Artemis.Core/Models/Profile/DataBindings/IDataBindingProperty.cs index 4b9ceeb94..c6f299766 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingProperty.cs @@ -5,7 +5,7 @@ namespace Artemis.Core /// /// Represents a data binding registration /// - public interface IDataBindingRegistration + public interface IDataBindingProperty { /// /// Gets or sets the display name of the data binding registration @@ -18,25 +18,15 @@ namespace Artemis.Core Type ValueType { get; } /// - /// Returns the data binding applied using this registration - /// - public IDataBinding? GetDataBinding(); - - /// - /// If found, creates a data binding from storage - /// - /// - IDataBinding? CreateDataBinding(); - - /// - /// If present, removes the current data binding - /// - void ClearDataBinding(); - - /// - /// Gets the value of the data binding + /// Gets the value of the property this registration points to /// /// A value matching the type of object? GetValue(); + + /// + /// Sets the value of the property this registration points to + /// + /// A value matching the type of + void SetValue(object? value); } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs index dcfcdeeda..9391c1027 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs @@ -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 /// LayerPropertyGroup LayerPropertyGroup { get; } + /// + /// Gets the data binding of this property + /// + IDataBinding BaseDataBinding { get; } + + /// + /// Gets a boolean indicating whether the layer has any data binding properties + /// + public bool HasDataBinding { get; } + + /// + /// Gets a boolean indicating whether data bindings are supported on this type of property + /// + public bool DataBindingsSupported { get; } + /// /// Gets the unique path of the property on the layer /// @@ -47,11 +61,6 @@ namespace Artemis.Core /// void Initialize(RenderProfileElement profileElement, LayerPropertyGroup group, PropertyEntity entity, bool fromStorage, PropertyDescriptionAttribute description, string path); - /// - /// Returns a list off all data binding registrations - /// - List GetAllDataBindingRegistrations(); - /// /// Attempts to load and add the provided keyframe entity to the layer property /// @@ -70,6 +79,12 @@ namespace Artemis.Core /// The timeline to apply to the property void Update(Timeline timeline); + + /// + /// Updates just the data binding instead of the entire layer + /// + void UpdateDataBinding(); + /// /// Occurs when the layer property is disposed /// @@ -104,25 +119,5 @@ namespace Artemis.Core /// Occurs when a keyframe was removed from the layer property /// public event EventHandler? KeyframeRemoved; - - /// - /// Occurs when a data binding property has been added - /// - public event EventHandler? DataBindingPropertyRegistered; - - /// - /// Occurs when all data binding properties have been removed - /// - public event EventHandler? DataBindingPropertiesCleared; - - /// - /// Occurs when a data binding has been enabled - /// - public event EventHandler? DataBindingEnabled; - - /// - /// Occurs when a data binding has been disabled - /// - public event EventHandler? DataBindingDisabled; } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 3d83ce37b..d75e47af0 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -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); } - /// - /// Invokes the event - /// - protected virtual void OnUpdated() - { - Updated?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnCurrentValueSet() - { - CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this)); - LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnVisibilityChanged() - { - VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnKeyframesToggled() - { - KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnKeyframeAdded() - { - KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnKeyframeRemoved() - { - KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingPropertyRegistered() - { - DataBindingPropertyRegistered?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingPropertiesCleared() - { - DataBindingPropertiesCleared?.Invoke(this, new LayerPropertyEventArgs(this)); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingEnabled(LayerPropertyEventArgs e) - { - DataBindingEnabled?.Invoke(this, e); - } - - /// - /// Invokes the event - /// - protected virtual void OnDataBindingDisabled(LayerPropertyEventArgs e) - { - DataBindingDisabled?.Invoke(this, e); - } - /// public PropertyDescriptionAttribute PropertyDescription { get; internal set; } @@ -162,7 +80,16 @@ namespace Artemis.Core CurrentValue = BaseValue; UpdateKeyframes(timeline); - UpdateDataBindings(timeline); + UpdateDataBinding(); + + // UpdateDataBinding called OnUpdated() + } + + /// + public void UpdateDataBinding() + { + DataBinding.Update(); + DataBinding.Apply(); OnUpdated(); } @@ -174,39 +101,6 @@ namespace Artemis.Core GC.SuppressFinalize(this); } - /// - public event EventHandler? Disposed; - - /// - public event EventHandler? Updated; - - /// - public event EventHandler? CurrentValueSet; - - /// - public event EventHandler? VisibilityChanged; - - /// - public event EventHandler? KeyframesToggled; - - /// - public event EventHandler? KeyframeAdded; - - /// - public event EventHandler? KeyframeRemoved; - - /// - public event EventHandler? DataBindingPropertyRegistered; - - /// - public event EventHandler? DataBindingPropertiesCleared; - - /// - public event EventHandler? DataBindingEnabled; - - /// - public event EventHandler? DataBindingDisabled; - #region Hierarchy private bool _isHidden; @@ -480,137 +374,19 @@ namespace Artemis.Core #region Data bindings - // ReSharper disable InconsistentNaming - internal readonly List _dataBindingRegistrations = new(); - - internal readonly List _dataBindings = new(); - // ReSharper restore InconsistentNaming - /// - /// Gets whether data bindings are supported on this type of property + /// Gets the data binding of this property /// - public bool DataBindingsSupported { get; protected internal set; } = true; + public DataBinding DataBinding { get; private set; } - /// - /// Gets whether the layer has any active data bindings - /// - public bool HasDataBinding => GetAllDataBindingRegistrations().Any(r => r.GetDataBinding() != null); + /// + public bool DataBindingsSupported => DataBinding.Properties.Any(); - /// - /// Gets a data binding registration by the display name used to register it - /// Note: The expression must exactly match the one used to register the data binding - /// - public DataBindingRegistration? GetDataBindingRegistration(string identifier) - { - if (_disposed) - throw new ObjectDisposedException("LayerProperty"); + /// + public IDataBinding BaseDataBinding => DataBinding; - IDataBindingRegistration? match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration registration && - registration.DisplayName == identifier); - return (DataBindingRegistration?) match; - } - - /// - /// Gets a list containing all data binding registrations of this layer property - /// - /// A list containing all data binding registrations of this layer property - public List GetAllDataBindingRegistrations() - { - return _dataBindingRegistrations; - } - - /// - /// Registers a data binding property so that is available to the data binding system - /// - /// The type of the layer property - /// The function to call to get the value of the property - /// The action to call to set the value of the property - /// The converter to use while applying the data binding - /// The display name of the data binding property - public DataBindingRegistration RegisterDataBindingProperty(Func getter, Action setter, DataBindingConverter 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 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; - } - - /// - /// Removes all data binding properties so they are no longer available to the data binding system - /// - public void ClearDataBindingProperties() - { - if (_disposed) - throw new ObjectDisposedException("LayerProperty"); - - foreach (IDataBindingRegistration dataBindingRegistration in _dataBindingRegistrations) - dataBindingRegistration.ClearDataBinding(); - _dataBindingRegistrations.Clear(); - - OnDataBindingPropertiesCleared(); - } - - /// - /// Enables a data binding for the provided - /// - /// The newly created data binding - public DataBinding EnableDataBinding(DataBindingRegistration 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 dataBinding = new(dataBindingRegistration); - _dataBindings.Add(dataBinding); - - OnDataBindingEnabled(new LayerPropertyEventArgs(dataBinding.LayerProperty)); - return dataBinding; - } - - /// - /// Disables the provided data binding - /// - /// The data binding to remove - public void DisableDataBinding(DataBinding 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(); - } - } + /// + 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(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(); } /// @@ -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; } /// @@ -775,5 +545,79 @@ namespace Artemis.Core } #endregion + + #region Events + + /// + public event EventHandler? Disposed; + + /// + public event EventHandler? Updated; + + /// + public event EventHandler? CurrentValueSet; + + /// + public event EventHandler? VisibilityChanged; + + /// + public event EventHandler? KeyframesToggled; + + /// + public event EventHandler? KeyframeAdded; + + /// + public event EventHandler? KeyframeRemoved; + + /// + /// Invokes the event + /// + protected virtual void OnUpdated() + { + Updated?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnCurrentValueSet() + { + CurrentValueSet?.Invoke(this, new LayerPropertyEventArgs(this)); + LayerPropertyGroup.OnLayerPropertyOnCurrentValueSet(new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnVisibilityChanged() + { + VisibilityChanged?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnKeyframesToggled() + { + KeyframesToggled?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnKeyframeAdded() + { + KeyframeAdded?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + /// + /// Invokes the event + /// + protected virtual void OnKeyframeRemoved() + { + KeyframeRemoved?.Invoke(this, new LayerPropertyEventArgs(this)); + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index f6a87f5b0..8cef4a100 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -113,11 +113,8 @@ namespace Artemis.Core ? new NodeScript($"Activate {_typeDisplayName}", $"Whether or not this {_typeDisplayName} should be active", RenderElementEntity.NodeScript, Profile) : new NodeScript($"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() diff --git a/src/Artemis.Core/VisualScripting/DataBindingNodeScript.cs b/src/Artemis.Core/VisualScripting/DataBindingNodeScript.cs new file mode 100644 index 000000000..198fa0cb8 --- /dev/null +++ b/src/Artemis.Core/VisualScripting/DataBindingNodeScript.cs @@ -0,0 +1,36 @@ +using System; +using Artemis.Core.Internal; +using Artemis.Storage.Entities.Profile.Nodes; + +namespace Artemis.Core +{ + public class DataBindingNodeScript : NodeScript + { + #region Properties & Fields + + internal DataBindingExitNode DataBindingExitNode { get; } + + /// + public override Type ResultType => typeof(object); + + #endregion + + /// + public DataBindingNodeScript(string name, string description, DataBinding dataBinding, object? context = null) + : base(name, description, context) + { + DataBindingExitNode = new DataBindingExitNode(dataBinding); + ExitNode = DataBindingExitNode; + AddNode(ExitNode); + } + + /// + public DataBindingNodeScript(string name, string description, DataBinding dataBinding, NodeScriptEntity entity, object? context = null) + : base(name, description, entity, context) + { + DataBindingExitNode = new DataBindingExitNode(dataBinding); + ExitNode = DataBindingExitNode; + AddNode(ExitNode); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/VisualScripting/InputPin.cs b/src/Artemis.Core/VisualScripting/InputPin.cs index 0244b6da3..39849a105 100644 --- a/src/Artemis.Core/VisualScripting/InputPin.cs +++ b/src/Artemis.Core/VisualScripting/InputPin.cs @@ -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 diff --git a/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs b/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs new file mode 100644 index 000000000..ef1102d85 --- /dev/null +++ b/src/Artemis.Core/VisualScripting/Internal/DataBindingExitNode.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Artemis.Core.Internal +{ + internal class DataBindingExitNode : Node, IExitNode + { + private readonly Dictionary _propertyPins = new(); + private readonly Dictionary _propertyValues = new(); + + public DataBinding DataBinding { get; } + + public DataBindingExitNode(DataBinding 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 + } +} \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs index 92a38fb1a..1160c38ae 100644 --- a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs @@ -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; } } } \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs index 6c04025d4..ec5fc0344 100644 --- a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs @@ -8,16 +8,15 @@ namespace Artemis.Storage.Entities.Profile public PropertyEntity() { KeyframeEntities = new List(); - DataBindingEntities = new List(); } 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 KeyframeEntities { get; set; } - public List DataBindingEntities { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/PropertyInput/PropertyInputViewModel.cs b/src/Artemis.UI.Shared/PropertyInput/PropertyInputViewModel.cs index a5e043888..f03156dd0 100644 --- a/src/Artemis.UI.Shared/PropertyInput/PropertyInputViewModel.cs +++ b/src/Artemis.UI.Shared/PropertyInput/PropertyInputViewModel.cs @@ -44,6 +44,11 @@ namespace Artemis.UI.Shared /// public LayerProperty LayerProperty { get; } + /// + /// Gets a boolean indicating whether the layer property should be enabled + /// + public bool IsEnabled => !LayerProperty.HasDataBinding; + /// /// Gets the profile editor service /// @@ -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(); } diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs index d0fdb6e3e..6f1b604eb 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs @@ -37,9 +37,9 @@ namespace Artemis.UI.Shared.Services RenderProfileElement? SelectedProfileElement { get; } /// - /// Gets the currently selected data binding property + /// Gets the currently selected data binding /// - ILayerProperty? SelectedDataBinding { get; } + IDataBinding? SelectedDataBinding { get; } /// /// Gets or sets the current time @@ -89,10 +89,10 @@ namespace Artemis.UI.Shared.Services void SaveSelectedProfileElement(); /// - /// Changes the selected data binding property + /// Changes the selected data binding /// - /// The data binding property to select - void ChangeSelectedDataBinding(ILayerProperty? layerProperty); + /// The data binding to select + void ChangeSelectedDataBinding(IDataBinding? dataBinding); /// /// Updates the profile preview, forcing UI-elements to re-render @@ -114,7 +114,7 @@ namespace Artemis.UI.Shared.Services /// /// Registers a new property input view model used in the profile editor for the generic type defined in /// - /// Note: Registration will remove itself on plugin disable so you don't have to + /// Note: DataBindingProperty will remove itself on plugin disable so you don't have to /// /// /// @@ -123,7 +123,7 @@ namespace Artemis.UI.Shared.Services /// /// Registers a new property input view model used in the profile editor for the generic type defined in /// - /// Note: Registration will remove itself on plugin disable so you don't have to + /// Note: DataBindingProperty will remove itself on plugin disable so you don't have to /// /// /// diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs index 37cccd627..b93760af4 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs @@ -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(); } diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputViewModel.cs index 1a3472a72..806d52084 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputViewModel.cs @@ -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 { - private List _registrations; - public BoolPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService) { - _registrations = layerProperty.GetAllDataBindingRegistrations(); } - public bool IsEnabled => _registrations.Any(r => r.GetDataBinding() != null); protected override void OnDataBindingsChanged() { diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml index 75b8ae201..09f2f664a 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml @@ -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" diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs index bafcc7f30..bdea29e96 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs @@ -13,8 +13,8 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class BrushPropertyInputViewModel : PropertyInputViewModel { - private readonly IPluginManagementService _pluginManagementService; private readonly IDialogService _dialogService; + private readonly IPluginManagementService _pluginManagementService; private BindableCollection _descriptors; public BrushPropertyInputViewModel(LayerProperty 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("LayerProperties", new Dictionary {{"layerBrush", layer.LayerBrush}}); }); - } } } diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs index c028b564d..f01f483ec 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputViewModel.cs @@ -8,19 +8,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class FloatPropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _registration; - public FloatPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _registration = layerProperty.GetDataBindingRegistration("Value"); - } - - public bool IsEnabled => _registration.DataBinding == null; - - protected override void OnDataBindingsChanged() - { - NotifyOfPropertyChange(nameof(IsEnabled)); } } diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml index c2c5d3501..cbb7dc9c5 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml @@ -27,7 +27,7 @@ Min="{Binding Start}" DragStarted="{s:Action InputDragStarted}" DragEnded="{s:Action InputDragEnded}" - IsEnabled="{Binding IsEndEnabled}" /> + IsEnabled="{Binding IsEnabled}" /> \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs index 20195e150..2a1271ad5 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs @@ -9,15 +9,10 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class FloatRangePropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _startRegistration; - private readonly DataBindingRegistration _endRegistration; - public FloatRangePropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _startRegistration = layerProperty.GetDataBindingRegistration("Start"); - _endRegistration = layerProperty.GetDataBindingRegistration("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 diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs index f729ef554..f972d5e3f 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputViewModel.cs @@ -8,19 +8,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class IntPropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _registration; - public IntPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _registration = layerProperty.GetDataBindingRegistration("Value"); - } - - public bool IsEnabled => _registration.DataBinding == null; - - protected override void OnDataBindingsChanged() - { - NotifyOfPropertyChange(nameof(IsEnabled)); } } diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml index da9440ce1..8c38caabf 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml @@ -18,7 +18,7 @@ Min="{Binding LayerProperty.PropertyDescription.MinInputValue}" DragStarted="{s:Action InputDragStarted}" DragEnded="{s:Action InputDragEnded}" - IsEnabled="{Binding IsStartEnabled}" /> + IsEnabled="{Binding IsEnabled}" /> - + IsEnabled="{Binding IsEnabled}" /> \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs index eed3e04ff..0358e383f 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs @@ -9,15 +9,10 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class IntRangePropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _startRegistration; - private readonly DataBindingRegistration _endRegistration; - public IntRangePropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _startRegistration = layerProperty.GetDataBindingRegistration("Start"); - _endRegistration = layerProperty.GetDataBindingRegistration("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 diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml index 026d4d381..bc2c308f9 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml @@ -17,10 +17,10 @@ + DragEnded="{s:Action InputDragEnded}" /> \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputViewModel.cs index 9bc2cc186..f2fb56579 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputViewModel.cs @@ -7,18 +7,8 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class SKColorPropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _registration; - public SKColorPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService) { - _registration = layerProperty.GetDataBindingRegistration("Value"); - } - - public bool IsEnabled => _registration.DataBinding == null; - - protected override void OnDataBindingsChanged() - { - NotifyOfPropertyChange(nameof(IsEnabled)); } } } \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml index 06cf966ca..55282ff67 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml @@ -18,7 +18,7 @@ Min="{Binding LayerProperty.PropertyDescription.MinInputValue}" DragStarted="{s:Action InputDragStarted}" DragEnded="{s:Action InputDragEnded}" - IsEnabled="{Binding IsXEnabled}" /> + IsEnabled="{Binding IsEnabled}" /> , + IsEnabled="{Binding IsEnabled}" /> \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs index ac32cbaf3..b4b4e4860 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs @@ -10,14 +10,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class SKPointPropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _xRegistration; - private readonly DataBindingRegistration _yRegistration; - public SKPointPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _xRegistration = layerProperty.GetDataBindingRegistration("X"); - _yRegistration = layerProperty.GetDataBindingRegistration("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 diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml index a15a2b38f..9fbf36a0c 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml @@ -18,7 +18,7 @@ Min="{Binding LayerProperty.PropertyDescription.MinInputValue}" DragStarted="{s:Action InputDragStarted}" DragEnded="{s:Action InputDragEnded}" - IsEnabled="{Binding IsHeightEnabled}" /> + IsEnabled="{Binding IsEnabled}" /> , + IsEnabled="{Binding IsEnabled}" /> \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputViewModel.cs index 48707f5c3..023cc700c 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputViewModel.cs @@ -12,14 +12,9 @@ namespace Artemis.UI.DefaultTypes.PropertyInput { public class SKSizePropertyInputViewModel : PropertyInputViewModel { - private readonly DataBindingRegistration _heightRegistration; - private readonly DataBindingRegistration _widthRegistration; - public SKSizePropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _widthRegistration = layerProperty.GetDataBindingRegistration("Width"); - _heightRegistration = layerProperty.GetDataBindingRegistration("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 diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index 7d1c85aa3..aaf677e09 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -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); diff --git a/src/Artemis.UI/Ninject/InstanceProviders/DataBindingsViewModelInstanceProvider.cs b/src/Artemis.UI/Ninject/InstanceProviders/DataBindingsViewModelInstanceProvider.cs deleted file mode 100644 index 3ab21713f..000000000 --- a/src/Artemis.UI/Ninject/InstanceProviders/DataBindingsViewModelInstanceProvider.cs +++ /dev/null @@ -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()); - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Ninject/UiModule.cs b/src/Artemis.UI/Ninject/UiModule.cs index 549b170c2..02a66a68f 100644 --- a/src/Artemis.UI/Ninject/UiModule.cs +++ b/src/Artemis.UI/Ninject/UiModule.cs @@ -56,7 +56,6 @@ namespace Artemis.UI.Ninject .BindToFactory(); }); - Kernel.Bind().ToFactory(() => new DataBindingsViewModelInstanceProvider()); Kernel.Bind().ToFactory(() => new LayerPropertyViewModelInstanceProvider()); // Bind profile editor VMs diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml deleted file mode 100644 index be5b8b20e..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - Enable data binding - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Result - - - Other bindings not updating? - - - - - - - - - - - - Input - - - Output - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml.cs deleted file mode 100644 index 2808d7a97..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows.Controls; - -namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings -{ - /// - /// Interaction logic for DataBindingView.xaml - /// - public partial class DataBindingView : UserControl - { - public DataBindingView() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs deleted file mode 100644 index 9b0a82562..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ /dev/null @@ -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 : 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 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(); - AvailableNodes = nodeService.AvailableNodes; - TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); - TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true); - } - - public DataBindingRegistration Registration { get; } - public NodeScript Script => Registration.DataBinding?.Script; - - public PluginSetting AlwaysApplyDataBindings { get; } - public BindableCollection EasingViewModels { get; } - public IEnumerable 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(); - 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().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; - } - - /// - public void Dispose() - { - _coreService.FrameRendered -= OnFrameRendered; - } - } - - public interface IDataBindingViewModel : IScreen, IDisposable - { - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml index 164f28796..90da2f349 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml @@ -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"> - - - - - - - - - - - - + d:DesignHeight="450" d:DesignWidth="800" + d:DataContext="{d:DesignInstance {x:Type layerProperties:DataBindingsViewModel}}"> + + + + + + + + + + + + + Enable data binding + + + + + + + + + Enable this data binding to use nodes! + + + When you enable data bindings you can no longer use keyframes or normal values for this property. + Instead you'll be using our node scripting system to set a value based on the data model or values of other layers/folders. + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs index aee7265a0..a2b4d0846 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs @@ -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.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 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 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; diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml index 12344aec7..9d0ea97d9 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml @@ -156,7 +156,6 @@ - diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs index e79e4325b..93a2e4d3b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs @@ -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)); } diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index b0549cdd7..40f94cf4e 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -30,7 +30,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization private bool _canSelectEditTool; private BindableCollection _devices; private BindableCollection _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(); } diff --git a/src/Artemis.VisualScripting/Nodes/CustomViewModels/LayerPropertyNodeCustomViewModel.cs b/src/Artemis.VisualScripting/Nodes/CustomViewModels/LayerPropertyNodeCustomViewModel.cs index 05706be9a..f18667794 100644 --- a/src/Artemis.VisualScripting/Nodes/CustomViewModels/LayerPropertyNodeCustomViewModel.cs +++ b/src/Artemis.VisualScripting/Nodes/CustomViewModels/LayerPropertyNodeCustomViewModel.cs @@ -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)); } diff --git a/src/Artemis.VisualScripting/Nodes/LayerPropertyNode.cs b/src/Artemis.VisualScripting/Nodes/LayerPropertyNode.cs index 64f75d022..e07c0b07c 100644 --- a/src/Artemis.VisualScripting/Nodes/LayerPropertyNode.cs +++ b/src/Artemis.VisualScripting/Nodes/LayerPropertyNode.cs @@ -26,16 +26,16 @@ namespace Artemis.VisualScripting.Nodes return; } - List list = LayerProperty.GetAllDataBindingRegistrations(); + List 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); }