From fac184c1a32a187212a30e085da629cf1ac99569 Mon Sep 17 00:00:00 2001 From: Robert Beekman Date: Wed, 24 Feb 2021 19:31:33 +0100 Subject: [PATCH 1/7] Force building RGB.NET release builds --- ci/azure-pipelines-rgbnet.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/azure-pipelines-rgbnet.yml b/ci/azure-pipelines-rgbnet.yml index db227c01b..1b8b0db88 100644 --- a/ci/azure-pipelines-rgbnet.yml +++ b/ci/azure-pipelines-rgbnet.yml @@ -37,6 +37,7 @@ steps: inputs: command: 'build' projects: '$(rgbSolution)' + arguments: '--configuration Release' - task: PublishPipelineArtifact@1 displayName: 'Upload build to Azure Pipelines' From 72f8175dc112d9c6c281388ebdd3f0a14953d8c2 Mon Sep 17 00:00:00 2001 From: Robert Beekman Date: Tue, 2 Mar 2021 19:26:27 +0100 Subject: [PATCH 2/7] Update README.md [skip ci] --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 90d3634c5..064e062dd 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ Artemis 1 is no longer supported and Artemis 2 is in active development. This en **Pre-release download**: https://github.com/SpoinkyNL/Artemis/releases (pre-release means your profiles may break at any given time!) **Plugin documentation**: https://artemis-rgb.com/docs/ -**Please note that even though we have plugins for each brand supported by RGB.NET, they have not been thoroughly tested. If you run into any issues please let us know on Discord.** +**Please note that even though we have plugins for each brand supported by RGB.NET, they have not been thoroughly tested. If you run into any issues please let us know on Discord.** +A full list of supported devices can be found on the wiki [here](https://wiki.artemis-rgb.com/en/guides/user/devices). #### Want to build? Follow these instructions 1. Create a central folder like ```C:\Repos``` From 267e8e6a1ec1471cebc7ce12be4427d01ed55add Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 2 Mar 2021 20:22:49 +0100 Subject: [PATCH 3/7] Data bindings - Rework internals to support more situations --- .../Converters/FloatDataBindingConverter.cs | 3 - .../Properties/BoolLayerProperty.cs | 2 +- .../Properties/ColorGradientLayerProperty.cs | 54 ++++++++- .../Properties/FloatLayerProperty.cs | 2 +- .../Properties/FloatRangeLayerProperty.cs | 6 +- .../Properties/IntLayerProperty.cs | 2 +- .../Properties/IntRangeLayerProperty.cs | 4 +- .../Properties/SKColorLayerProperty.cs | 2 +- .../Properties/SKPointLayerProperty.cs | 5 +- .../Properties/SKSizeLayerProperty.cs | 4 +- .../Profile/DataBindings/DataBinding.cs | 4 +- .../DataBindings/DataBindingConverter.cs | 108 +----------------- .../DataBindings/DataBindingRegistration.cs | 32 +++--- .../Profile/LayerProperties/LayerProperty.cs | 27 +++-- .../DataBindings/DataBindingViewModel.cs | 6 +- .../Visualization/ProfileViewModel.cs | 2 +- 16 files changed, 109 insertions(+), 154 deletions(-) diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs index b076134bb..75818ac95 100644 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs +++ b/src/Artemis.Core/DefaultTypes/DataBindings/Converters/FloatDataBindingConverter.cs @@ -36,9 +36,6 @@ namespace Artemis.Core /// public override void ApplyValue(float value) { - if (ValueTypeSetExpression == null) - return; - if (DataBinding!.LayerProperty.PropertyDescription.MaxInputValue is float max) value = Math.Min(value, max); if (DataBinding!.LayerProperty.PropertyDescription.MinInputValue is float min) diff --git a/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs index bfeb477f4..567ef6002 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs @@ -6,7 +6,7 @@ internal BoolLayerProperty() { KeyframesSupported = false; - RegisterDataBindingProperty(b => b, new GeneralDataBindingConverter()); + RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new GeneralDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs index 449461bec..39f02c655 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs @@ -1,4 +1,6 @@ -namespace Artemis.Core +using SkiaSharp; + +namespace Artemis.Core { /// public class ColorGradientLayerProperty : LayerProperty @@ -6,12 +8,33 @@ internal ColorGradientLayerProperty() { KeyframesSupported = false; - DataBindingsSupported = false; + DataBindingsSupported = true; DefaultValue = new ColorGradient(); - + CurrentValueSet += OnCurrentValueSet; } + private void CreateDataBindingRegistrations() + { + ClearDataBindingProperties(); + if (CurrentValue == null) + return; + + for (int index = 0; index < CurrentValue.Stops.Count; index++) + { + int stopIndex = index; + DataBindingRegistration registerDataBindingProperty = RegisterDataBindingProperty( + gradient => gradient.Stops[stopIndex].Color, + (gradient, value) => gradient.Stops[stopIndex].Color = value, + new ColorStopDataBindingConverter(), + $"Color #{stopIndex + 1}" + ); + + registerDataBindingProperty.DisplayName = $"Color #{stopIndex + 1}"; + } + } + + /// /// Implicitly converts an to a /// @@ -31,6 +54,8 @@ // Don't allow color gradients to be null if (BaseValue == null) BaseValue = DefaultValue ?? new ColorGradient(); + + CreateDataBindingRegistrations(); } #region Overrides of LayerProperty @@ -41,10 +66,31 @@ // Don't allow color gradients to be null if (BaseValue == null) BaseValue = DefaultValue ?? new ColorGradient(); - + base.OnInitialize(); } #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/FloatLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs index 55221deaf..589244657 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs @@ -5,7 +5,7 @@ { internal FloatLayerProperty() { - RegisterDataBindingProperty(value => value, new FloatDataBindingConverter()); + RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new FloatDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs index 4b54aca95..74baa44f9 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs @@ -5,9 +5,9 @@ { internal FloatRangeLayerProperty() { - RegisterDataBindingProperty(range => range.Start, new FloatDataBindingConverter()); - RegisterDataBindingProperty(range => range.End, new FloatDataBindingConverter()); - + RegisterDataBindingProperty(value => value.Start, (value, newValue) => value.Start = newValue, new FloatDataBindingConverter(), "Start"); + RegisterDataBindingProperty(value => value.End, (value, newValue) => value.End = newValue, new FloatDataBindingConverter(), "End"); + CurrentValueSet += OnCurrentValueSet; } diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs index 7edd5241d..8d1fff755 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs @@ -7,7 +7,7 @@ namespace Artemis.Core { internal IntLayerProperty() { - RegisterDataBindingProperty(value => value, new IntDataBindingConverter()); + RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new IntDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs index 2a9f498fe..8ca36f876 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs @@ -5,8 +5,8 @@ { internal IntRangeLayerProperty() { - RegisterDataBindingProperty(range => range.Start, new IntDataBindingConverter()); - RegisterDataBindingProperty(range => range.End, new IntDataBindingConverter()); + RegisterDataBindingProperty(value => value.Start, (value, newValue) => value.Start = newValue, new IntDataBindingConverter(), "Start"); + RegisterDataBindingProperty(value => value.End, (value, newValue) => value.End = newValue, new IntDataBindingConverter(), "End"); CurrentValueSet += OnCurrentValueSet; } diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs index 366c4aa49..e08b9aaf6 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs @@ -7,7 +7,7 @@ namespace Artemis.Core { internal SKColorLayerProperty() { - RegisterDataBindingProperty(value => value, new SKColorDataBindingConverter()); + RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new SKColorDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs index 74befb209..f32a312c0 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs @@ -7,8 +7,9 @@ namespace Artemis.Core { internal SKPointLayerProperty() { - RegisterDataBindingProperty(point => point.X, new FloatDataBindingConverter()); - RegisterDataBindingProperty(point => point.Y, new FloatDataBindingConverter()); + RegisterDataBindingProperty(value => value.X, (value, newValue) => value.X = newValue, new FloatDataBindingConverter(), "X"); + RegisterDataBindingProperty(value => value.Y, (value, newValue) => value.Y = newValue, new FloatDataBindingConverter(), "Y"); + } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs index 0d98286b6..d018b4fb5 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs @@ -7,8 +7,8 @@ namespace Artemis.Core { internal SKSizeLayerProperty() { - RegisterDataBindingProperty(size => size.Height, new FloatDataBindingConverter()); - RegisterDataBindingProperty(size => size.Width, new FloatDataBindingConverter()); + RegisterDataBindingProperty(value => value.Height, (value, newValue) => value.Height = newValue, new FloatDataBindingConverter(), "Height"); + RegisterDataBindingProperty(value => value.Width, (value, newValue) => value.Width = newValue, new FloatDataBindingConverter(), "Width"); } /// diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index 18c18894c..44370e498 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -97,7 +97,7 @@ namespace Artemis.Core /// public Type? GetTargetType() { - return Registration?.PropertyExpression.ReturnType; + return Registration?.Getter.Method.ReturnType; } private void ResetEasing(TProperty value) @@ -293,7 +293,7 @@ namespace Artemis.Core // Don't save an invalid state if (Registration != null) - Entity.TargetExpression = Registration.PropertyExpression.ToString(); + Entity.TargetExpression = Registration.Getter.ToString(); Entity.EasingTime = EasingTime; Entity.EasingFunction = (int) EasingFunction; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs index fe4254c22..a7ec330ed 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs @@ -1,6 +1,4 @@ using System; -using System.Linq.Expressions; -using System.Reflection; namespace Artemis.Core { @@ -10,21 +8,6 @@ namespace Artemis.Core /// public abstract class DataBindingConverter : IDataBindingConverter { - /// - /// Gets a dynamically compiled getter pointing to the data bound property - /// - public Func? GetExpression { get; private set; } - - /// - /// Gets a dynamically compiled setter pointing to the data bound property used for value types - /// - public Action? ValueTypeSetExpression { get; private set; } - - /// - /// Gets a dynamically compiled setter pointing to the data bound property used for reference types - /// - public Action? ReferenceTypeSetExpression { get; private set; } - /// /// Gets the data binding this converter is applied to /// @@ -40,9 +23,6 @@ namespace Artemis.Core /// public bool SupportsInterpolate { get; protected set; } - /// - public Type SupportedType => typeof(TProperty); - /// /// Returns the sum of and /// @@ -65,12 +45,9 @@ namespace Artemis.Core /// public virtual void ApplyValue(TProperty value) { - if (DataBinding == null) + if (DataBinding?.Registration == null) throw new ArtemisCoreException("Data binding converter is not yet initialized"); - if (ReferenceTypeSetExpression != null) - ReferenceTypeSetExpression(DataBinding.LayerProperty.CurrentValue, value); - else if (ValueTypeSetExpression != null) - ValueTypeSetExpression(value); + DataBinding.Registration.Setter(DataBinding.LayerProperty.CurrentValue, value); } /// @@ -78,9 +55,9 @@ namespace Artemis.Core /// public virtual TProperty GetValue() { - if (DataBinding == null || GetExpression == null) + if (DataBinding?.Registration == null) throw new ArtemisCoreException("Data binding converter is not yet initialized"); - return GetExpression(DataBinding.LayerProperty.CurrentValue); + return DataBinding.Registration.Getter(DataBinding.LayerProperty.CurrentValue); } /// @@ -104,83 +81,10 @@ namespace Artemis.Core throw new ArtemisCoreException("Cannot initialize a data binding converter for a data binding without a registration"); DataBinding = dataBinding; - GetExpression = dataBinding.Registration.PropertyExpression.Compile(); - CreateSetExpression(); - OnInitialized(); } - private void CreateSetExpression() - { - // If the registration does not point towards a member of LayerProperty.CurrentValue, assign directly to LayerProperty.CurrentValue - if (DataBinding!.Registration?.Member == null) - { - CreateSetCurrentValueExpression(); - return; - } - - // Ensure the member of LayerProperty.CurrentValue has a setter - MethodInfo? setterMethod = null; - if (DataBinding.Registration.Member is PropertyInfo propertyInfo) - setterMethod = propertyInfo.GetSetMethod(); - // If there is no setter, the built-in data binding cannot do its job, stay null - if (setterMethod == null) - return; - - // If LayerProperty.CurrentValue is a value type, assign it directly to LayerProperty.CurrentValue after applying the changes - if (typeof(TLayerProperty).IsValueType) - CreateSetValueTypeExpression(); - // If it is a reference type it can safely be updated by its reference - else - CreateSetReferenceTypeExpression(); - } - - private void CreateSetReferenceTypeExpression() - { - if (DataBinding!.Registration?.Member == null) - throw new ArtemisCoreException("Cannot create value setter for data binding without a registration"); - - ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); - ParameterExpression parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue"); - MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, DataBinding.Registration.Member); - BinaryExpression assignment = Expression.Assign(memberAccess, propertyValue); - Expression> referenceTypeLambda = Expression.Lambda>(assignment, parameter, propertyValue); - - ReferenceTypeSetExpression = referenceTypeLambda.Compile(); - } - - private void CreateSetValueTypeExpression() - { - if (DataBinding!.Registration?.Member == null) - throw new ArtemisCoreException("Cannot create value setter for data binding without a registration"); - - ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); - ParameterExpression variableCurrent = Expression.Variable(typeof(TLayerProperty), "current"); - ConstantExpression layerProperty = Expression.Constant(DataBinding.LayerProperty); - MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, - DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); - - BlockExpression body = Expression.Block( - new[] {variableCurrent}, - Expression.Assign(variableCurrent, layerPropertyMemberAccess), - Expression.Assign(Expression.MakeMemberAccess(variableCurrent, DataBinding.Registration.Member), propertyValue), - Expression.Assign(layerPropertyMemberAccess, variableCurrent) - ); - - Expression> valueTypeLambda = Expression.Lambda>(body, propertyValue); - ValueTypeSetExpression = valueTypeLambda.Compile(); - } - - private void CreateSetCurrentValueExpression() - { - ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); - ConstantExpression layerProperty = Expression.Constant(DataBinding!.LayerProperty); - MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, - DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); - - BinaryExpression body = Expression.Assign(layerPropertyMemberAccess, propertyValue); - Expression> lambda = Expression.Lambda>(body, propertyValue); - ValueTypeSetExpression = lambda.Compile(); - } + /// + public Type SupportedType => typeof(TProperty); } } \ 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 index 00346e68b..e43c28550 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs @@ -1,7 +1,5 @@ using System; using System.Linq; -using System.Linq.Expressions; -using System.Reflection; using Artemis.Storage.Entities.Profile.DataBindings; namespace Artemis.Core @@ -9,16 +7,16 @@ namespace Artemis.Core /// public class DataBindingRegistration : IDataBindingRegistration { - internal DataBindingRegistration(LayerProperty layerProperty, - DataBindingConverter converter, - Expression> propertyExpression) + 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)); - PropertyExpression = propertyExpression ?? throw new ArgumentNullException(nameof(propertyExpression)); - - if (propertyExpression.Body is MemberExpression memberExpression) - Member = memberExpression.Member; + Getter = getter ?? throw new ArgumentNullException(nameof(getter)); + Setter = setter ?? throw new ArgumentNullException(nameof(setter)); + DisplayName = displayName ?? throw new ArgumentNullException(nameof(displayName)); } /// @@ -32,15 +30,19 @@ namespace Artemis.Core public DataBindingConverter Converter { get; } /// - /// Gets the expression that that accesses the property + /// Gets the function to call to get the value of the property /// - public Expression> PropertyExpression { get; } + public Func Getter { get; } /// - /// Gets the member the targets - /// if the is not a member expression + /// Gets the action to call to set the value of the property /// - public MemberInfo? Member { get; } + public Action Setter { get; } + + /// + /// Gets or sets the display name of the data binding registration + /// + public string DisplayName { get; set; } /// /// Gets the data binding created using this registration @@ -59,7 +61,7 @@ namespace Artemis.Core if (DataBinding != null) return DataBinding; - DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetExpression == PropertyExpression.ToString()); + DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetExpression == Getter.ToString()); if (dataBinding == null) return null; diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index bb9760a91..b184f6e24 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -393,7 +393,7 @@ namespace Artemis.Core throw new ObjectDisposedException("LayerProperty"); IDataBindingRegistration? match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration registration && - registration.PropertyExpression.ToString() == expression); + registration.Getter.ToString() == expression); return (DataBindingRegistration?) match; } @@ -410,21 +410,30 @@ namespace Artemis.Core /// Registers a data binding property so that is available to the data binding system /// /// The type of the layer property - /// The expression pointing to the value to register + /// 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 - public void RegisterDataBindingProperty(Expression> propertyExpression, DataBindingConverter converter) + /// 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 (propertyExpression.Body.NodeType != ExpressionType.MemberAccess && propertyExpression.Body.NodeType != ExpressionType.Parameter) - throw new ArtemisCoreException("Provided expression is invalid, it must be 'value => value' or 'value => value.Property'"); + DataBindingRegistration registration = new(this, converter, getter, setter, displayName); + _dataBindingRegistrations.Add(registration); + return registration; + } - if (converter.SupportedType != propertyExpression.ReturnType) - throw new ArtemisCoreException($"Cannot register data binding property for property {PropertyDescription.Name} " + - "because the provided converter does not support the property's type"); + /// + /// 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"); - _dataBindingRegistrations.Add(new DataBindingRegistration(this, converter, propertyExpression)); + _dataBindingRegistrations.Clear(); } /// diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs index 0ba67c4ab..105af6c65 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs @@ -38,11 +38,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings _profileEditorService = profileEditorService; _dataBindingsVmFactory = dataBindingsVmFactory; - if (Registration.Member != null) - DisplayName = Registration.Member.Name.ToUpper(); - else - DisplayName = Registration.LayerProperty.PropertyDescription.Name.ToUpper(); - + DisplayName = Registration.DisplayName.ToUpper(); AlwaysApplyDataBindings = settingsService.GetSetting("ProfileEditor.AlwaysApplyDataBindings", true); DataBindingModes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType))); EasingViewModels = new BindableCollection(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index 071884dcd..f3620ba15 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -302,7 +302,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization TimeSpan delta = DateTime.Now - _lastUpdate; _lastUpdate = DateTime.Now; - if (!AlwaysApplyDataBindings.Value || _profileEditorService.SelectedProfile == null || _profileEditorService.Playing) + if (!AlwaysApplyDataBindings.Value || _profileEditorService.SelectedProfile == null) return; foreach (IDataBindingRegistration dataBindingRegistration in _profileEditorService.SelectedProfile.GetAllFolders() From f4ad41cbd45f9b3e73fe8b5ef0762fdf23245daf Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 2 Mar 2021 21:41:56 +0100 Subject: [PATCH 4/7] Data bindings - Simplified getter/setter code --- .../Properties/BoolLayerProperty.cs | 2 +- .../Properties/ColorGradientLayerProperty.cs | 8 +++----- .../Properties/FloatLayerProperty.cs | 2 +- .../Properties/FloatRangeLayerProperty.cs | 6 +++--- .../DefaultTypes/Properties/IntLayerProperty.cs | 2 +- .../Properties/IntRangeLayerProperty.cs | 4 ++-- .../Properties/SKColorLayerProperty.cs | 2 +- .../Properties/SKPointLayerProperty.cs | 5 ++--- .../Properties/SKSizeLayerProperty.cs | 4 ++-- .../Models/Profile/DataBindings/DataBinding.cs | 4 ++-- .../DataBindings/DataBindingConverter.cs | 4 ++-- .../DataBindings/DataBindingRegistration.cs | 16 ++++++---------- .../DataBindings/IDataBindingRegistration.cs | 5 +++++ .../Profile/LayerProperties/LayerProperty.cs | 17 +++++++---------- .../Profile/DataBindings/DataBindingEntity.cs | 2 +- .../PropertyInput/BoolPropertyInputView.xaml | 2 +- .../PropertyInput/BoolPropertyInputViewModel.cs | 16 +++++++++++++--- .../PropertyInput/BrushPropertyInputView.xaml | 4 ++-- .../BrushPropertyInputViewModel.cs | 2 +- .../ColorGradientPropertyInputView.xaml | 3 +-- .../ColorGradientPropertyInputViewModel.cs | 2 +- .../PropertyInput/EnumPropertyInputView.xaml | 2 +- .../PropertyInput/EnumPropertyInputViewModel.cs | 2 +- .../PropertyInput/FloatPropertyInputView.xaml | 3 +-- .../FloatPropertyInputViewModel.cs | 4 ++-- .../FloatRangePropertyInputView.xaml | 3 +-- .../FloatRangePropertyInputViewModel.cs | 6 +++--- .../PropertyInput/IntPropertyInputView.xaml | 3 +-- .../PropertyInput/IntPropertyInputViewModel.cs | 4 ++-- .../IntRangePropertyInputView.xaml | 3 +-- .../IntRangePropertyInputViewModel.cs | 6 +++--- .../PropertyInput/SKColorPropertyInputView.xaml | 3 +-- .../SKColorPropertyInputViewModel.cs | 4 ++-- .../PropertyInput/SKPointPropertyInputView.xaml | 3 +-- .../SKPointPropertyInputViewModel.cs | 6 +++--- .../PropertyInput/SKSizePropertyInputView.xaml | 3 +-- .../SKSizePropertyInputViewModel.cs | 6 +++--- src/Artemis.UI/Services/RegistrationService.cs | 2 +- 38 files changed, 86 insertions(+), 89 deletions(-) diff --git a/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs index 567ef6002..99a7b614a 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/BoolLayerProperty.cs @@ -6,7 +6,7 @@ internal BoolLayerProperty() { KeyframesSupported = false; - RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new GeneralDataBindingConverter(), "Value"); + RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new GeneralDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs index 39f02c655..931e4d03d 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs @@ -23,14 +23,12 @@ namespace Artemis.Core for (int index = 0; index < CurrentValue.Stops.Count; index++) { int stopIndex = index; - DataBindingRegistration registerDataBindingProperty = RegisterDataBindingProperty( - gradient => gradient.Stops[stopIndex].Color, - (gradient, value) => gradient.Stops[stopIndex].Color = value, + RegisterDataBindingProperty( + () => CurrentValue.Stops[stopIndex].Color, + value => CurrentValue.Stops[stopIndex].Color = value, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}" ); - - registerDataBindingProperty.DisplayName = $"Color #{stopIndex + 1}"; } } diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs index 589244657..cb1a6eae8 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs @@ -5,7 +5,7 @@ { internal FloatLayerProperty() { - RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new FloatDataBindingConverter(), "Value"); + RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new FloatDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs index 74baa44f9..298e824f3 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs @@ -5,9 +5,9 @@ { internal FloatRangeLayerProperty() { - RegisterDataBindingProperty(value => value.Start, (value, newValue) => value.Start = newValue, new FloatDataBindingConverter(), "Start"); - RegisterDataBindingProperty(value => value.End, (value, newValue) => value.End = newValue, new FloatDataBindingConverter(), "End"); - + RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new FloatDataBindingConverter(), "Start"); + RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new FloatDataBindingConverter(), "End"); + CurrentValueSet += OnCurrentValueSet; } diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs index 8d1fff755..f69a1bdbc 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/IntLayerProperty.cs @@ -7,7 +7,7 @@ namespace Artemis.Core { internal IntLayerProperty() { - RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new IntDataBindingConverter(), "Value"); + RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new IntDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs index 8ca36f876..474fa33e1 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs @@ -5,8 +5,8 @@ { internal IntRangeLayerProperty() { - RegisterDataBindingProperty(value => value.Start, (value, newValue) => value.Start = newValue, new IntDataBindingConverter(), "Start"); - RegisterDataBindingProperty(value => value.End, (value, newValue) => value.End = newValue, new IntDataBindingConverter(), "End"); + RegisterDataBindingProperty(() => CurrentValue.Start, value => CurrentValue.Start = value, new IntDataBindingConverter(), "Start"); + RegisterDataBindingProperty(() => CurrentValue.End, value => CurrentValue.End = value, new IntDataBindingConverter(), "End"); CurrentValueSet += OnCurrentValueSet; } diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs index e08b9aaf6..46370568a 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKColorLayerProperty.cs @@ -7,7 +7,7 @@ namespace Artemis.Core { internal SKColorLayerProperty() { - RegisterDataBindingProperty(value => value, (_, newValue) => CurrentValue = newValue, new SKColorDataBindingConverter(), "Value"); + RegisterDataBindingProperty(() => CurrentValue, value => CurrentValue = value, new SKColorDataBindingConverter(), "Value"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs index f32a312c0..3cd654ad9 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKPointLayerProperty.cs @@ -7,9 +7,8 @@ namespace Artemis.Core { internal SKPointLayerProperty() { - RegisterDataBindingProperty(value => value.X, (value, newValue) => value.X = newValue, new FloatDataBindingConverter(), "X"); - RegisterDataBindingProperty(value => value.Y, (value, newValue) => value.Y = newValue, new FloatDataBindingConverter(), "Y"); - + 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"); } /// diff --git a/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs index d018b4fb5..3274c26f9 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/SKSizeLayerProperty.cs @@ -7,8 +7,8 @@ namespace Artemis.Core { internal SKSizeLayerProperty() { - RegisterDataBindingProperty(value => value.Height, (value, newValue) => value.Height = newValue, new FloatDataBindingConverter(), "Height"); - RegisterDataBindingProperty(value => value.Width, (value, newValue) => value.Width = newValue, new FloatDataBindingConverter(), "Width"); + 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"); } /// diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index 44370e498..4bfbacc48 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -272,7 +272,7 @@ namespace Artemis.Core throw new ObjectDisposedException("DataBinding"); // General - DataBindingRegistration? registration = LayerProperty.GetDataBindingRegistration(Entity.TargetExpression); + DataBindingRegistration? registration = LayerProperty.GetDataBindingRegistration(Entity.Identifier); if (registration != null) ApplyRegistration(registration); @@ -293,7 +293,7 @@ namespace Artemis.Core // Don't save an invalid state if (Registration != null) - Entity.TargetExpression = Registration.Getter.ToString(); + Entity.Identifier = Registration.DisplayName; Entity.EasingTime = EasingTime; Entity.EasingFunction = (int) EasingFunction; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs index a7ec330ed..2b3d7eb5a 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs @@ -47,7 +47,7 @@ namespace Artemis.Core { if (DataBinding?.Registration == null) throw new ArtemisCoreException("Data binding converter is not yet initialized"); - DataBinding.Registration.Setter(DataBinding.LayerProperty.CurrentValue, value); + DataBinding.Registration.Setter(value); } /// @@ -57,7 +57,7 @@ namespace Artemis.Core { if (DataBinding?.Registration == null) throw new ArtemisCoreException("Data binding converter is not yet initialized"); - return DataBinding.Registration.Getter(DataBinding.LayerProperty.CurrentValue); + return DataBinding.Registration.Getter(); } /// diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs index e43c28550..78e233fe1 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingRegistration.cs @@ -8,9 +8,7 @@ namespace Artemis.Core public class DataBindingRegistration : IDataBindingRegistration { internal DataBindingRegistration(LayerProperty layerProperty, DataBindingConverter converter, - Func getter, - Action setter, - string displayName) + Func getter, Action setter, string displayName) { LayerProperty = layerProperty ?? throw new ArgumentNullException(nameof(layerProperty)); Converter = converter ?? throw new ArgumentNullException(nameof(converter)); @@ -32,17 +30,15 @@ namespace Artemis.Core /// /// Gets the function to call to get the value of the property /// - public Func Getter { get; } + public Func Getter { get; } /// /// Gets the action to call to set the value of the property /// - public Action Setter { get; } + public Action Setter { get; } - /// - /// Gets or sets the display name of the data binding registration - /// - public string DisplayName { get; set; } + /// + public string DisplayName { get; } /// /// Gets the data binding created using this registration @@ -61,7 +57,7 @@ namespace Artemis.Core if (DataBinding != null) return DataBinding; - DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.TargetExpression == Getter.ToString()); + DataBindingEntity? dataBinding = LayerProperty.Entity.DataBindingEntities.FirstOrDefault(e => e.Identifier == DisplayName); if (dataBinding == null) return null; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs b/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs index fb6220951..8e1de1f0a 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/IDataBindingRegistration.cs @@ -5,6 +5,11 @@ /// public interface IDataBindingRegistration { + /// + /// Gets or sets the display name of the data binding registration + /// + string DisplayName { get; } + /// /// Returns the data binding applied using this registration /// diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index b184f6e24..fe93cbcb3 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -379,21 +379,16 @@ namespace Artemis.Core public bool HasDataBinding => GetAllDataBindingRegistrations().Any(r => r.GetDataBinding() != null); /// - /// Gets a data binding registration by the expression used to register it + /// 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(Expression> propertyExpression) - { - return GetDataBindingRegistration(propertyExpression.ToString()); - } - - internal DataBindingRegistration? GetDataBindingRegistration(string expression) + public DataBindingRegistration? GetDataBindingRegistration(string identifier) { if (_disposed) throw new ObjectDisposedException("LayerProperty"); IDataBindingRegistration? match = _dataBindingRegistrations.FirstOrDefault(r => r is DataBindingRegistration registration && - registration.Getter.ToString() == expression); + registration.DisplayName == identifier); return (DataBindingRegistration?) match; } @@ -414,13 +409,15 @@ namespace Artemis.Core /// 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, + 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); + DataBindingRegistration registration = new(this, converter, getter, setter, displayName); _dataBindingRegistrations.Add(registration); return registration; } diff --git a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs index 0a0dc030c..6dd27b85a 100644 --- a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs @@ -4,7 +4,7 @@ namespace Artemis.Storage.Entities.Profile.DataBindings { public class DataBindingEntity { - public string TargetExpression { get; set; } + public string Identifier { get; set; } public TimeSpan EasingTime { get; set; } public int EasingFunction { get; set; } diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.xaml index 16f480241..ebf357f9e 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BoolPropertyInputView.xaml @@ -1,4 +1,4 @@ - { + private List _registrations; + public BoolPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService) { + _registrations = layerProperty.GetAllDataBindingRegistrations(); } - public bool IsEnabled => true; + public bool IsEnabled => _registrations.Any(r => r.GetDataBinding() != null); + + protected override void OnDataBindingsChanged() + { + NotifyOfPropertyChange(nameof(IsEnabled)); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml index 15c14f988..aa4e288d3 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputView.xaml @@ -1,12 +1,12 @@ - diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs index d0b5e1b16..20409eb1c 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/BrushPropertyInputViewModel.cs @@ -7,7 +7,7 @@ using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using Stylet; -namespace Artemis.UI.PropertyInput +namespace Artemis.UI.DefaultTypes.PropertyInput { public class BrushPropertyInputViewModel : PropertyInputViewModel { diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.xaml index 13ea3a9c9..ef17ba34e 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/ColorGradientPropertyInputView.xaml @@ -1,9 +1,8 @@ - { diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.xaml index 732d1648f..818914562 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/EnumPropertyInputView.xaml @@ -1,4 +1,4 @@ - : PropertyInputViewModel where T : Enum { diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputView.xaml index 3c9bd2105..65e27826f 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatPropertyInputView.xaml @@ -1,11 +1,10 @@ - { @@ -13,7 +13,7 @@ namespace Artemis.UI.PropertyInput public FloatPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _registration = layerProperty.GetDataBindingRegistration(value => value); + _registration = layerProperty.GetDataBindingRegistration("Value"); } public bool IsEnabled => _registration.DataBinding == null; diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml index e88828009..aabe66a33 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml @@ -1,9 +1,8 @@ - { @@ -16,8 +16,8 @@ namespace Artemis.UI.PropertyInput IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _startRegistration = layerProperty.GetDataBindingRegistration(range => range.Start); - _endRegistration = layerProperty.GetDataBindingRegistration(range => range.End); + _startRegistration = layerProperty.GetDataBindingRegistration("Start"); + _endRegistration = layerProperty.GetDataBindingRegistration("End"); } public float Start diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputView.xaml index 14fab93f1..4cae90771 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntPropertyInputView.xaml @@ -1,11 +1,10 @@ - { @@ -13,7 +13,7 @@ namespace Artemis.UI.PropertyInput public IntPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _registration = layerProperty.GetDataBindingRegistration(value => value); + _registration = layerProperty.GetDataBindingRegistration("Value"); } public bool IsEnabled => _registration.DataBinding == null; diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml index 62e4b8570..40a0e815a 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml @@ -1,9 +1,8 @@ - { @@ -16,8 +16,8 @@ namespace Artemis.UI.PropertyInput IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _startRegistration = layerProperty.GetDataBindingRegistration(range => range.Start); - _endRegistration = layerProperty.GetDataBindingRegistration(range => range.End); + _startRegistration = layerProperty.GetDataBindingRegistration("Start"); + _endRegistration = layerProperty.GetDataBindingRegistration("End"); } public int Start diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml index fdc5a6c1b..6861259bb 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKColorPropertyInputView.xaml @@ -1,10 +1,9 @@ - { @@ -11,7 +11,7 @@ namespace Artemis.UI.PropertyInput public SKColorPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService) : base(layerProperty, profileEditorService) { - _registration = layerProperty.GetDataBindingRegistration(value => value); + _registration = layerProperty.GetDataBindingRegistration("Value"); } public bool IsEnabled => _registration.DataBinding == null; diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml index 34e679a0c..1f15efc15 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputView.xaml @@ -1,11 +1,10 @@ - diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs index 8d7448f8b..ac32cbaf3 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKPointPropertyInputViewModel.cs @@ -6,7 +6,7 @@ using FluentValidation; using SkiaSharp; using Stylet; -namespace Artemis.UI.PropertyInput +namespace Artemis.UI.DefaultTypes.PropertyInput { public class SKPointPropertyInputViewModel : PropertyInputViewModel { @@ -16,8 +16,8 @@ namespace Artemis.UI.PropertyInput public SKPointPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _xRegistration = layerProperty.GetDataBindingRegistration(point => point.X); - _yRegistration = layerProperty.GetDataBindingRegistration(point => point.Y); + _xRegistration = layerProperty.GetDataBindingRegistration("X"); + _yRegistration = layerProperty.GetDataBindingRegistration("Y"); } public float X diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml index 9cca2e62b..0f5dfc93c 100644 --- a/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/SKSizePropertyInputView.xaml @@ -1,9 +1,8 @@ - { @@ -18,8 +18,8 @@ namespace Artemis.UI.PropertyInput public SKSizePropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, IModelValidator validator) : base(layerProperty, profileEditorService, validator) { - _widthRegistration = layerProperty.GetDataBindingRegistration(size => size.Width); - _heightRegistration = layerProperty.GetDataBindingRegistration(size => size.Height); + _widthRegistration = layerProperty.GetDataBindingRegistration("Width"); + _heightRegistration = layerProperty.GetDataBindingRegistration("Height"); } // Since SKSize is immutable we need to create properties that replace the SKSize entirely diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs index ffbdc2958..0d51ce376 100644 --- a/src/Artemis.UI/Services/RegistrationService.cs +++ b/src/Artemis.UI/Services/RegistrationService.cs @@ -4,9 +4,9 @@ using Artemis.Core.Services; using Artemis.UI.Controllers; using Artemis.UI.DefaultTypes.DataModel.Display; using Artemis.UI.DefaultTypes.DataModel.Input; +using Artemis.UI.DefaultTypes.PropertyInput; using Artemis.UI.InputProviders; using Artemis.UI.Ninject; -using Artemis.UI.PropertyInput; using Artemis.UI.Shared.Services; using Serilog; From ef665115648015d018d061d9ec6f021bd13890b4 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 4 Mar 2021 19:17:17 +0100 Subject: [PATCH 5/7] Data bindings - Update UI on registration changes Data bindings - Tweaked UI tabs to handle a large amount of properties better Color gradient property - Update data binding properties on stop add/remove --- .../Properties/ColorGradientLayerProperty.cs | 19 ++++++- .../Profile/LayerProperties/ILayerProperty.cs | 10 ++++ .../Profile/LayerProperties/LayerProperty.cs | 25 +++++++++ .../DataBindings/DataBindingViewModel.cs | 2 +- .../DataBindings/DataBindingsView.xaml | 9 +++- .../DataBindings/DataBindingsViewModel.cs | 54 +++++++++++++++++-- 6 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs index 931e4d03d..6eb4bdda2 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs @@ -1,10 +1,13 @@ -using SkiaSharp; +using System.ComponentModel; +using SkiaSharp; namespace Artemis.Core { /// public class ColorGradientLayerProperty : LayerProperty { + private ColorGradient? _subscribedGradient; + internal ColorGradientLayerProperty() { KeyframesSupported = false; @@ -53,9 +56,23 @@ namespace Artemis.Core if (BaseValue == null) BaseValue = DefaultValue ?? new ColorGradient(); + if (_subscribedGradient != BaseValue) + { + if (_subscribedGradient != null) + _subscribedGradient.PropertyChanged -= SubscribedGradientOnPropertyChanged; + _subscribedGradient = BaseValue; + _subscribedGradient.PropertyChanged += SubscribedGradientOnPropertyChanged; + } + CreateDataBindingRegistrations(); } + private void SubscribedGradientOnPropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (CurrentValue.Stops.Count != GetAllDataBindingRegistrations().Count) + CreateDataBindingRegistrations(); + } + #region Overrides of LayerProperty /// diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs index 33153f4ef..c42b1f74c 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/ILayerProperty.cs @@ -97,6 +97,16 @@ namespace Artemis.Core /// 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 /// diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index fe93cbcb3..61f38c428 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -419,6 +419,8 @@ namespace Artemis.Core DataBindingRegistration registration = new(this, converter, getter, setter, displayName); _dataBindingRegistrations.Add(registration); + + OnDataBindingPropertyRegistered(); return registration; } @@ -431,6 +433,7 @@ namespace Artemis.Core throw new ObjectDisposedException("LayerProperty"); _dataBindingRegistrations.Clear(); + OnDataBindingPropertiesCleared(); } /// @@ -667,6 +670,12 @@ namespace Artemis.Core /// public event EventHandler? KeyframeRemoved; + /// + public event EventHandler? DataBindingPropertyRegistered; + + /// + public event EventHandler? DataBindingPropertiesCleared; + /// public event EventHandler? DataBindingEnabled; @@ -722,6 +731,22 @@ namespace Artemis.Core 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 /// diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs index 105af6c65..b25710928 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs @@ -102,8 +102,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings protected override void OnInitialActivate() { - base.OnInitialActivate(); Initialize(); + base.OnInitialActivate(); } private void Initialize() diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml index 35e1c740c..164f28796 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml @@ -6,11 +6,18 @@ xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + + + + + + \ 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 23e73dcd1..00c72fc34 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Artemis.Core; using Artemis.UI.Ninject.Factories; using Artemis.UI.Shared.Services; @@ -11,7 +14,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IProfileEditorService _profileEditorService; + private ILayerProperty? _selectedDataBinding; private int _selectedItemIndex; + private bool _updating; public DataBindingsViewModel(IProfileEditorService profileEditorService, IDataBindingsVmFactory dataBindingsVmFactory) { @@ -24,9 +29,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings get => _selectedItemIndex; set => SetAndNotify(ref _selectedItemIndex, value); } - + private void CreateDataBindingViewModels() { + int oldIndex = SelectedItemIndex; Items.Clear(); ILayerProperty layerProperty = _profileEditorService.SelectedDataBinding; @@ -37,26 +43,64 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings // Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving // and creating the actual data bindings - foreach (IDataBindingRegistration registration in registrations) - Items.Add(_dataBindingsVmFactory.DataBindingViewModel(registration)); + Items.AddRange(registrations.Select(registration => _dataBindingsVmFactory.DataBindingViewModel(registration))); - SelectedItemIndex = 0; + SelectedItemIndex = Items.Count < oldIndex ? 0 : oldIndex; } private void ProfileEditorServiceOnSelectedDataBindingChanged(object sender, EventArgs e) { CreateDataBindingViewModels(); + SubscribeToSelectedDataBinding(); + + SelectedItemIndex = 0; + } + + private void SubscribeToSelectedDataBinding() + { + if (_selectedDataBinding != null) + { + _selectedDataBinding.DataBindingPropertyRegistered -= DataBindingRegistrationsChanged; + _selectedDataBinding.DataBindingPropertiesCleared -= DataBindingRegistrationsChanged; + } + + _selectedDataBinding = _profileEditorService.SelectedDataBinding; + if (_selectedDataBinding != null) + { + _selectedDataBinding.DataBindingPropertyRegistered += DataBindingRegistrationsChanged; + _selectedDataBinding.DataBindingPropertiesCleared += DataBindingRegistrationsChanged; + } + } + + private void DataBindingRegistrationsChanged(object sender, LayerPropertyEventArgs e) + { + if (_updating) + return; + + _updating = true; + Execute.PostToUIThread(async () => + { + await Task.Delay(200); + CreateDataBindingViewModels(); + _updating = false; + }); } #region Overrides of Screen - /// protected override void OnInitialActivate() { _profileEditorService.SelectedDataBindingChanged += ProfileEditorServiceOnSelectedDataBindingChanged; CreateDataBindingViewModels(); + SubscribeToSelectedDataBinding(); base.OnInitialActivate(); } + + protected override void OnActivate() + { + SelectedItemIndex = 0; + base.OnActivate(); + } protected override void OnClose() { From 6ba3e6941d8c12da33f6533ee79aa2f6c9e8b318 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 4 Mar 2021 20:02:07 +0100 Subject: [PATCH 6/7] Color gradient property - Trigger PropertyChanged on data binding update --- .../Properties/ColorGradientLayerProperty.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs index 6eb4bdda2..387c2d9ef 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/ColorGradientLayerProperty.cs @@ -26,12 +26,14 @@ namespace Artemis.Core for (int index = 0; index < CurrentValue.Stops.Count; index++) { int stopIndex = index; - RegisterDataBindingProperty( - () => CurrentValue.Stops[stopIndex].Color, - value => CurrentValue.Stops[stopIndex].Color = value, - new ColorStopDataBindingConverter(), - $"Color #{stopIndex + 1}" - ); + + void Setter(SKColor value) + { + CurrentValue.Stops[stopIndex].Color = value; + CurrentValue.OnColorValuesUpdated(); + } + + RegisterDataBindingProperty(() => CurrentValue.Stops[stopIndex].Color, Setter, new ColorStopDataBindingConverter(), $"Color #{stopIndex + 1}"); } } @@ -58,12 +60,12 @@ namespace Artemis.Core if (_subscribedGradient != BaseValue) { - if (_subscribedGradient != null) + if (_subscribedGradient != null) _subscribedGradient.PropertyChanged -= SubscribedGradientOnPropertyChanged; _subscribedGradient = BaseValue; _subscribedGradient.PropertyChanged += SubscribedGradientOnPropertyChanged; } - + CreateDataBindingRegistrations(); } From a57dda8485f1680ca663ad18f4581671febb1517 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 4 Mar 2021 20:05:23 +0100 Subject: [PATCH 7/7] Data bindings - Added migration to identifier-based storage --- .../Migrations/M10BetterDataBindings.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/Artemis.Storage/Migrations/M10BetterDataBindings.cs diff --git a/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs b/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs new file mode 100644 index 000000000..4b7daf20f --- /dev/null +++ b/src/Artemis.Storage/Migrations/M10BetterDataBindings.cs @@ -0,0 +1,55 @@ +using Artemis.Storage.Migrations.Interfaces; +using LiteDB; + +namespace Artemis.Storage.Migrations +{ + public class M10BetterDataBindings : IStorageMigration + { + private void Migrate(BsonValue bsonValue) + { + if (!bsonValue.IsDocument || !bsonValue.AsDocument.TryGetValue("PropertyEntities", out BsonValue propertyEntities)) + return; + + foreach (BsonValue propertyEntity in propertyEntities.AsArray) + { + if (!propertyEntity.AsDocument.TryGetValue("DataBindingEntities", out BsonValue dataBindingEntities)) + continue; + foreach (BsonValue dataBindingEntity in dataBindingEntities.AsArray) + { + if (!dataBindingEntity.AsDocument.TryGetValue("TargetExpression", out BsonValue targetExpression)) + continue; + string value = targetExpression.AsString; + if (value == "value => value" || value == "b => b") + { + dataBindingEntity.AsDocument["Identifier"] = "Value"; + } + else + { + string selector = value.Split("=>")[1]; + string property = selector.Split(".")[1]; + dataBindingEntity.AsDocument["Identifier"] = property; + } + + dataBindingEntity.AsDocument.Remove("TargetExpression"); + } + } + } + + public int UserVersion => 10; + + public void Apply(LiteRepository repository) + { + ILiteCollection collection = repository.Database.GetCollection("ProfileEntity"); + foreach (BsonDocument bsonDocument in collection.FindAll()) + { + foreach (BsonValue bsonLayer in bsonDocument["Layers"].AsArray) + Migrate(bsonLayer); + + foreach (BsonValue bsonLayer in bsonDocument["Folders"].AsArray) + Migrate(bsonLayer); + + collection.Update(bsonDocument); + } + } + } +} \ No newline at end of file