From 18225ca6fac4510397ac7af87379d6d9ba9ac87d Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 4 Sep 2020 23:12:48 +0200 Subject: [PATCH] Data bindings - Fixed some conversion issues Data bindings - Started hooking up the UI --- .../LayerPropertyGroupUpdatingEventArgs.cs | 14 +++ .../Profile/DataBindings/DataBinding.cs | 36 ++++-- .../DataBindings/DataBindingModifier.cs | 7 -- .../Modifiers/DataBindingModifierType.cs | 5 - src/Artemis.Core/Models/Profile/Folder.cs | 4 +- src/Artemis.Core/Models/Profile/Layer.cs | 16 +-- .../Profile/LayerProperties/LayerProperty.cs | 38 +++--- .../Models/Profile/LayerPropertyGroup.cs | 10 +- .../Ninject/Factories/IVMFactory.cs | 12 +- .../DataBindings/DataBindingModifierView.xaml | 40 ++++-- .../DataBindingModifierViewModel.cs | 116 +++++++++++++++++- .../DataBindings/DataBindingView.xaml | 22 ++-- .../DataBindings/DataBindingView.xaml.cs | 26 ++++ .../DataBindings/DataBindingViewModel.cs | 81 ++++++++++-- .../DataBindings/DataBindingsViewModel.cs | 9 +- .../LayerPropertiesViewModel.cs | 11 +- 16 files changed, 357 insertions(+), 90 deletions(-) create mode 100644 src/Artemis.Core/Events/LayerPropertyGroupUpdatingEventArgs.cs create mode 100644 src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml.cs diff --git a/src/Artemis.Core/Events/LayerPropertyGroupUpdatingEventArgs.cs b/src/Artemis.Core/Events/LayerPropertyGroupUpdatingEventArgs.cs new file mode 100644 index 000000000..fc0e51cbe --- /dev/null +++ b/src/Artemis.Core/Events/LayerPropertyGroupUpdatingEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace Artemis.Core +{ + public class LayerPropertyGroupUpdatingEventArgs : EventArgs + { + public LayerPropertyGroupUpdatingEventArgs(double deltaTime) + { + DeltaTime = deltaTime; + } + + public double DeltaTime { 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 10a2c206f..9442c07b9 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -132,11 +132,13 @@ namespace Artemis.Core /// public object GetValue(object baseValue) { - if (baseValue.GetType() != TargetProperty.PropertyType) - { - throw new ArtemisCoreException($"The provided current value type ({baseValue.GetType().Name}) not match the " + - $"target property type ({TargetProperty.PropertyType.Name})"); - } + // Validating this is kinda expensive, it'll fail on ChangeType later anyway ^^ + // var targetType = TargetProperty.PropertyType; + // if (!targetType.IsCastableFrom(baseValue.GetType())) + // { + // throw new ArtemisCoreException($"The provided current value type ({baseValue.GetType().Name}) not match the " + + // $"target property type ({targetType.Name})"); + // } if (CompiledTargetAccessor == null) return baseValue; @@ -145,7 +147,11 @@ namespace Artemis.Core foreach (var dataBindingModifier in Modifiers) dataBindingValue = dataBindingModifier.Apply(dataBindingValue); - return dataBindingValue; + return Convert.ChangeType(dataBindingValue, TargetProperty.PropertyType); + } + + internal void Update(double deltaTime) + { } internal void ApplyToEntity() @@ -210,20 +216,32 @@ namespace Artemis.Core if (listType != null) throw new ArtemisCoreException($"Cannot create a regular accessor at path {SourcePropertyPath} because the path contains a list"); - var parameter = Expression.Parameter(typeof(object), "targetDataModel"); + var parameter = Expression.Parameter(typeof(DataModel), "targetDataModel"); var accessor = SourcePropertyPath.Split('.').Aggregate( Expression.Convert(parameter, SourceDataModel.GetType()), // Cast to the appropriate type Expression.Property ); - var lambda = Expression.Lambda>(accessor); + var returnValue = Expression.Convert(accessor, typeof(object)); + + var lambda = Expression.Lambda>(returnValue, parameter); CompiledTargetAccessor = lambda.Compile(); } } + /// + /// A mode that determines how the data binding is applied to the layer property + /// public enum DataBindingMode { - Override, + /// + /// Replaces the layer property value with the data binding value + /// + Replace, + + /// + /// Adds the data binding value to the layer property value + /// Add } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs index 6710cf8e9..24ee17a39 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs @@ -100,13 +100,6 @@ namespace Artemis.Core /// The modified value public object Apply(object currentValue) { - var targetType = DataBinding.TargetProperty.GetType(); - if (currentValue.GetType() != targetType) - { - throw new ArtemisCoreException("The current value of the data binding does not match the type of the target property." + - $" {targetType.Name} expected, received {currentValue.GetType().Name}."); - } - if (CompiledDynamicPredicate != null) return CompiledDynamicPredicate(currentValue, ParameterDataModel); if (CompiledStaticPredicate != null) diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs index ecac3e009..59ca364ac 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs @@ -20,11 +20,6 @@ namespace Artemis.Core /// public PluginInfo PluginInfo { get; internal set; } - /// - /// Gets the data binding modifier this modifier type is applied to - /// - public DataBindingModifier Modifier { get; internal set; } - /// /// Gets the types this modifier supports /// diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs index e10744485..5671181ca 100644 --- a/src/Artemis.Core/Models/Profile/Folder.cs +++ b/src/Artemis.Core/Models/Profile/Folder.cs @@ -79,7 +79,7 @@ namespace Artemis.Core foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { - baseLayerEffect.BaseProperties?.Update(); + baseLayerEffect.BaseProperties?.Update(deltaTime); baseLayerEffect.Update(deltaTime); } @@ -125,7 +125,7 @@ namespace Artemis.Core foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { - baseLayerEffect.BaseProperties?.Update(); + baseLayerEffect.BaseProperties?.Update(delta); baseLayerEffect.Update(delta); } } diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index a1f3595e6..6830bf239 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -261,14 +261,14 @@ namespace Artemis.Core if (TimelinePosition > TimelineLength) return; - General.Update(); - Transform.Update(); - LayerBrush.BaseProperties?.Update(); + General.Update(deltaTime); + Transform.Update(deltaTime); + LayerBrush.BaseProperties?.Update(deltaTime); LayerBrush.Update(deltaTime); foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { - baseLayerEffect.BaseProperties?.Update(); + baseLayerEffect.BaseProperties?.Update(deltaTime); baseLayerEffect.Update(deltaTime); } } @@ -301,14 +301,14 @@ namespace Artemis.Core var delta = (TimelinePosition - beginTime).TotalSeconds; - General.Update(); - Transform.Update(); - LayerBrush.BaseProperties?.Update(); + General.Update(delta); + Transform.Update(delta); + LayerBrush.BaseProperties?.Update(delta); LayerBrush.Update(delta); foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { - baseLayerEffect.BaseProperties?.Update(); + baseLayerEffect.BaseProperties?.Update(delta); baseLayerEffect.Update(delta); } } diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 8680a6214..65a16ac6c 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -19,7 +19,6 @@ namespace Artemis.Core public class LayerProperty : BaseLayerProperty { private T _baseValue; - private T _currentValue; private bool _isInitialized; private List> _keyframes; @@ -47,11 +46,7 @@ namespace Artemis.Core /// /// Gets the current value of this property as it is affected by it's keyframes, updated once every frame /// - public T CurrentValue - { - get => !KeyframesEnabled || !KeyframesSupported ? BaseValue : _currentValue; - internal set => _currentValue = value; - } + public T CurrentValue { get; set; } /// /// Gets or sets the default value of this layer property. If set, this value is automatically applied if the property @@ -100,7 +95,7 @@ namespace Artemis.Core currentKeyframe.Value = value; // Update the property so that the new keyframe is reflected on the current value - Update(); + Update(0); } } @@ -190,9 +185,19 @@ namespace Artemis.Core } /// - /// Updates the property, applying keyframes to the current value + /// Updates the property, applying keyframes and data bindings to the current value /// - internal void Update() + internal void Update(double deltaTime) + { + CurrentValue = BaseValue; + + UpdateKeyframes(); + UpdateDataBindings(deltaTime); + + OnUpdated(); + } + + private void UpdateKeyframes() { if (!KeyframesSupported || !KeyframesEnabled) return; @@ -216,8 +221,15 @@ namespace Artemis.Core var keyframeProgressEased = (float) Easings.Interpolate(keyframeProgress, CurrentKeyframe.EasingFunction); UpdateCurrentValue(keyframeProgress, keyframeProgressEased); } + } - OnUpdated(); + private void UpdateDataBindings(double deltaTime) + { + foreach (var dataBinding in DataBindings) + { + dataBinding.Update(deltaTime); + ApplyDataBinding(dataBinding); + } } /// @@ -237,7 +249,7 @@ namespace Artemis.Core PropertyEntity = entity; LayerPropertyGroup = layerPropertyGroup; - LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(); + LayerPropertyGroup.PropertyGroupUpdating += (sender, args) => Update(args.DeltaTime); try { @@ -303,9 +315,7 @@ namespace Artemis.Core /// protected override void ApplyDataBinding(DataBinding dataBinding) { - // The default implementation only supports simple types - if (dataBinding.TargetProperty.DeclaringType == GetType()) - CurrentValue = (T) dataBinding.GetValue(CurrentValue); + CurrentValue = (T) dataBinding.GetValue(CurrentValue); } #endregion diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs index 2a05e7daf..2d2a9231a 100644 --- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs +++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs @@ -227,11 +227,11 @@ namespace Artemis.Core } } - internal void Update() + internal void Update(double deltaTime) { // Since at this point we don't know what properties the group has without using reflection, // let properties subscribe to the update event and update themselves - OnPropertyGroupUpdating(); + OnPropertyGroupUpdating(new LayerPropertyGroupUpdatingEventArgs(deltaTime)); } private void InitializeProperty(RenderProfileElement profileElement, string path, BaseLayerProperty instance) @@ -264,7 +264,7 @@ namespace Artemis.Core #region Events - internal event EventHandler PropertyGroupUpdating; + internal event EventHandler PropertyGroupUpdating; /// /// Occurs when the property group has initialized all its children @@ -282,9 +282,9 @@ namespace Artemis.Core /// public event EventHandler VisibilityChanged; - protected virtual void OnPropertyGroupUpdating() + protected virtual void OnPropertyGroupUpdating(LayerPropertyGroupUpdatingEventArgs e) { - PropertyGroupUpdating?.Invoke(this, EventArgs.Empty); + PropertyGroupUpdating?.Invoke(this, e); } protected virtual void OnVisibilityChanged() diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index 6a2c359f9..137a6d77d 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -1,4 +1,5 @@ -using Artemis.Core; +using System.Reflection; +using Artemis.Core; using Artemis.Core.Modules; using Artemis.UI.Screens.Modules; using Artemis.UI.Screens.Modules.Tabs; @@ -7,6 +8,7 @@ using Artemis.UI.Screens.ProfileEditor.DisplayConditions; using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract; using Artemis.UI.Screens.ProfileEditor.LayerProperties; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Abstract; +using Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings; using Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree; @@ -17,6 +19,7 @@ using Artemis.UI.Screens.Settings.Debug; using Artemis.UI.Screens.Settings.Tabs.Devices; using Artemis.UI.Screens.Settings.Tabs.Plugins; using Stylet; +using Module = Artemis.Core.Modules.Module; namespace Artemis.UI.Ninject.Factories { @@ -75,6 +78,13 @@ namespace Artemis.UI.Ninject.Factories DisplayConditionListPredicateViewModel DisplayConditionListPredicateViewModel(DisplayConditionListPredicate displayConditionListPredicate, DisplayConditionViewModel parent); } + public interface IDataBindingsVmFactory : IVmFactory + { + DataBindingsViewModel DataBindingsViewModel(BaseLayerProperty layerProperty); + DataBindingViewModel DataBindingViewModel(BaseLayerProperty layerProperty, PropertyInfo targetProperty); + DataBindingModifierViewModel DataBindingModifierViewModel(DataBindingModifier modifier); + } + public interface ILayerPropertyVmFactory : IVmFactory { LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription); diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml index b9bb6eb77..f8edd20cf 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml @@ -6,9 +6,30 @@ xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings" xmlns:s="https://github.com/canton7/Stylet" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" + xmlns:converters="clr-namespace:Artemis.UI.Converters" + xmlns:utilities="clr-namespace:Artemis.UI.Utilities" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:DataBindingModifierViewModel}"> + + + + + + + + + + + + + + + + + + + @@ -41,10 +62,10 @@ Style="{StaticResource DisplayConditionButtonLeftClickMenu}" Background="#7B7B7B" BorderBrush="#7B7B7B" - Content="{Binding SelectedOperator.Description}"> + Content="{Binding SelectedModifierType.Description}"> - + @@ -55,7 +76,7 @@ @@ -63,18 +84,19 @@ +