From f03ea410d4df9fac266a08b98166e30244b31466 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Mon, 14 Sep 2020 19:16:32 +0200 Subject: [PATCH] Data bindings - Finished main functionality --- .../Converters/FloatDataBindingConverter.cs | 20 +--- .../Converters/GeneralDataBindingConverter.cs | 12 --- .../Converters/IntDataBindingConverter.cs | 12 --- .../SKColorArgbDataBindingConverter.cs | 19 +--- .../Profile/DataBindings/DataBinding.cs | 4 +- .../DataBindings/DataBindingConverter.cs | 100 ++++++++++++++---- .../DataBindings/DataBindingModifier.cs | 8 +- .../Abstract/DataBindingModifierType.cs | 8 +- .../Modifiers/DivideModifierType.cs | 5 + .../Modifiers/MultiplicationModifier.cs | 5 + src/Artemis.Core/Models/Profile/Layer.cs | 4 +- .../Input/DataModelDynamicViewModel.cs | 9 +- .../Screens/Modules/ModuleRootViewModel.cs | 2 +- .../DataBindings/DataBindingModifierView.xaml | 18 ++-- .../DataBindingModifierViewModel.cs | 41 +++++-- .../DataBindings/DataBindingView.xaml | 2 +- .../DataBindings/DataBindingViewModel.cs | 39 +++++-- .../LayerPropertyGroupViewModel.cs | 1 + .../Timeline/TimelineEasingViewModel.cs | 3 +- .../Timeline/TimelineGroupViewModel.cs | 2 +- .../ProfileTree/ProfileTreeViewModel.cs | 13 +-- .../Visualization/ProfileViewModel.cs | 15 ++- src/Artemis.UI/Screens/RootViewModel.cs | 7 +- 23 files changed, 214 insertions(+), 135 deletions(-) diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Converters/FloatDataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/Converters/FloatDataBindingConverter.cs index 7a0c3d0a8..ed77db48f 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Converters/FloatDataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Converters/FloatDataBindingConverter.cs @@ -30,14 +30,14 @@ namespace Artemis.Core /// public override float Interpolate(float a, float b, double progress) { - var diff = a - b; + var diff = b - a; return (float) (a + diff * progress); } /// public override void ApplyValue(float value) { - if (SetExpression == null) + if (ValueTypeSetExpression == null) return; if (DataBinding.LayerProperty.PropertyDescription.MaxInputValue is float max) @@ -45,21 +45,7 @@ namespace Artemis.Core if (DataBinding.LayerProperty.PropertyDescription.MinInputValue is float min) value = Math.Max(value, min); - var test = new SKSize(5,5); - test.Width = 10; - - SetExpression(DataBinding.LayerProperty.CurrentValue, value); - } - - private void Mehtest(ref SKSize test) - { - test.Width = 20; - } - - /// - public override float GetValue() - { - return GetExpression(DataBinding.LayerProperty.CurrentValue); + base.ApplyValue(value); } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Converters/GeneralDataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/Converters/GeneralDataBindingConverter.cs index bb94f06ac..478b17eea 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Converters/GeneralDataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Converters/GeneralDataBindingConverter.cs @@ -22,17 +22,5 @@ namespace Artemis.Core { throw new NotSupportedException(); } - - /// - public override void ApplyValue(object value) - { - SetExpression?.Invoke(DataBinding.LayerProperty.CurrentValue, value); - } - - /// - public override object GetValue() - { - return GetExpression(DataBinding.LayerProperty.CurrentValue); - } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Converters/IntDataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/Converters/IntDataBindingConverter.cs index 70daff304..86265461f 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Converters/IntDataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Converters/IntDataBindingConverter.cs @@ -37,17 +37,5 @@ namespace Artemis.Core var diff = b - a; return (int) Math.Round(a + diff * progress, InterpolationRoundingMode); } - - /// - public override void ApplyValue(int value) - { - SetExpression?.Invoke(DataBinding.LayerProperty.CurrentValue, value); - } - - /// - public override int GetValue() - { - return GetExpression(DataBinding.LayerProperty.CurrentValue); - } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Converters/Internal/SKColorArgbDataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/Converters/Internal/SKColorArgbDataBindingConverter.cs index cc8c6c551..e713bd23a 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Converters/Internal/SKColorArgbDataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Converters/Internal/SKColorArgbDataBindingConverter.cs @@ -24,7 +24,7 @@ namespace Artemis.Core public override byte Interpolate(byte a, byte b, double progress) { var diff = b - a; - return ClampToByte(diff * progress); + return ClampToByte(a + diff * progress); } public override void ApplyValue(byte value) @@ -48,21 +48,10 @@ namespace Artemis.Core } } - public override byte GetValue() + public override byte ConvertFromObject(object source) { - switch (_channel) - { - case Channel.Alpha: - return DataBinding.LayerProperty.CurrentValue.Alpha; - case Channel.Red: - return DataBinding.LayerProperty.CurrentValue.Red; - case Channel.Green: - return DataBinding.LayerProperty.CurrentValue.Green; - case Channel.Blue: - return DataBinding.LayerProperty.CurrentValue.Blue; - default: - throw new ArgumentOutOfRangeException(); - } + var saveValue = Convert.ToDouble(source); + return ClampToByte(saveValue); } private static byte ClampToByte(double value) diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs index d584a802a..f167832fe 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs @@ -206,8 +206,8 @@ namespace Artemis.Core foreach (var dataBindingModifier in Modifiers) dataBindingValue = dataBindingModifier.Apply(dataBindingValue); - var value = (TProperty) Convert.ChangeType(dataBindingValue, typeof(TProperty)); - + TProperty value = Converter.ConvertFromObject(dataBindingValue); + // If no easing is to be applied simple return whatever the current value is if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate) return value; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs index fbe7642fc..380caba36 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingConverter.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -19,7 +18,9 @@ namespace Artemis.Core /// /// A dynamically compiled setter pointing to the data bound property /// - public Action SetExpression { get; private set; } + public Action ValueTypeSetExpression { get; private set; } + + public Action ReferenceTypeSetExpression { get; private set; } /// /// Gets the data binding this converter is applied to @@ -59,12 +60,29 @@ namespace Artemis.Core /// Applies the to the layer property /// /// - public abstract void ApplyValue(TProperty value); + public virtual void ApplyValue(TProperty value) + { + if (ReferenceTypeSetExpression != null) + ReferenceTypeSetExpression(DataBinding.LayerProperty.CurrentValue, value); + else if (ValueTypeSetExpression != null) + ValueTypeSetExpression(value); + } /// /// Returns the current base value of the data binding /// - public abstract TProperty GetValue(); + public virtual TProperty GetValue() + { + return GetExpression(DataBinding.LayerProperty.CurrentValue); + } + + /// + /// 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 @@ -77,40 +95,76 @@ namespace Artemis.Core { DataBinding = dataBinding; GetExpression = dataBinding.Registration.PropertyExpression.Compile(); - SetExpression = CreateValueSetter(); + CreateSetExpression(); OnInitialized(); } - private Action CreateValueSetter() + private void CreateSetExpression() { - MethodInfo setterMethod = null; - - if (DataBinding.Registration.Member != null) + // If the registration does not point towards a member of LayerProperty.CurrentValue, assign directly to LayerProperty.CurrentValue + if (DataBinding.Registration.Member == null) { - if (DataBinding.Registration.Member is PropertyInfo propertyInfo) - setterMethod = propertyInfo.GetSetMethod(); + 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 null; + return; - var parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue"); + // 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() + { var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); - - + var parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue"); var memberAccess = Expression.MakeMemberAccess(parameter, DataBinding.Registration.Member); var assignment = Expression.Assign(memberAccess, propertyValue); - var lambda = Expression.Lambda>(assignment, parameter, propertyValue); + var referenceTypeLambda = Expression.Lambda>(assignment, parameter, propertyValue); - if (typeof(TLayerProperty).IsValueType) - { - // var layerProperty = Expression.Constant(DataBinding.LayerProperty); - // var layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); - // var assingment = Expression.Assign() - } + ReferenceTypeSetExpression = referenceTypeLambda.Compile(); + } - return lambda.Compile(); + private void CreateSetValueTypeExpression() + { + var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); + var variableCurrent = Expression.Variable(typeof(TLayerProperty), "current"); + var layerProperty = Expression.Constant(DataBinding.LayerProperty); + var layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, + DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); + + var body = Expression.Block( + new[] {variableCurrent}, + Expression.Assign(variableCurrent, layerPropertyMemberAccess), + Expression.Assign(Expression.MakeMemberAccess(variableCurrent, DataBinding.Registration.Member), propertyValue), + Expression.Assign(layerPropertyMemberAccess, variableCurrent) + ); + + var valueTypeLambda = Expression.Lambda>(body, propertyValue); + ValueTypeSetExpression = valueTypeLambda.Compile(); + } + + private void CreateSetCurrentValueExpression() + { + var propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue"); + var layerProperty = Expression.Constant(DataBinding.LayerProperty); + var layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty, + DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]); + + var body = Expression.Assign(layerPropertyMemberAccess, propertyValue); + var lambda = Expression.Lambda>(body, propertyValue); + ValueTypeSetExpression = lambda.Compile(); } } } \ 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 05e126771..5ad50d0ee 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs @@ -16,18 +16,20 @@ namespace Artemis.Core /// /// Creates a new instance of the class /// + /// The data binding the modifier is to be applied to /// The type of the parameter, can either be dynamic (based on a data model value) or static - public DataBindingModifier(ProfileRightSideType parameterType) + public DataBindingModifier(DataBinding dataBinding, ProfileRightSideType parameterType) { + _dataBinding = dataBinding ?? throw new ArgumentNullException(nameof(dataBinding)); ParameterType = parameterType; Entity = new DataBindingModifierEntity(); - Save(); Initialize(); + Save(); } internal DataBindingModifier(DataBinding dataBinding, DataBindingModifierEntity entity) { - DataBinding = dataBinding; + _dataBinding = dataBinding; Entity = entity; Load(); Initialize(); diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/Abstract/DataBindingModifierType.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/Abstract/DataBindingModifierType.cs index 7f08e7a5a..bf0210834 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/Abstract/DataBindingModifierType.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/Abstract/DataBindingModifierType.cs @@ -31,10 +31,16 @@ namespace Artemis.Core public abstract string Icon { get; } /// - /// Gets or sets whether this modifier supports a parameter, defaults to true + /// Gets or sets whether this modifier supports a parameter, defaults to true /// public bool SupportsParameter { get; protected set; } = true; + /// + /// Gets or sets the preferred parameter type + /// If null, the parameter type will match the source property + /// + public Type PreferredParameterType { get; protected set; } = null; + /// /// Returns whether the given type is supported by the modifier /// diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DivideModifierType.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DivideModifierType.cs index 7f153d14b..0497fde96 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DivideModifierType.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DivideModifierType.cs @@ -5,6 +5,11 @@ namespace Artemis.Core { internal class DivideModifierType : DataBindingModifierType { + public DivideModifierType() + { + PreferredParameterType = typeof(float); + } + public override IReadOnlyCollection CompatibleTypes => Constants.NumberTypes; public override string Description => "Divide by"; diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/MultiplicationModifier.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/MultiplicationModifier.cs index bf400778d..fe71f7ccc 100644 --- a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/MultiplicationModifier.cs +++ b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/MultiplicationModifier.cs @@ -5,6 +5,11 @@ namespace Artemis.Core { internal class MultiplicationModifierType : DataBindingModifierType { + public MultiplicationModifierType() + { + PreferredParameterType = typeof(float); + } + public override IReadOnlyCollection CompatibleTypes => Constants.NumberTypes; public override string Description => "Multiply by"; diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 7fdad81f5..97f705548 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -682,9 +682,9 @@ namespace Artemis.Core } // Ensure the brush reference matches the brush - var current = General.BrushReference.CurrentValue; + var current = General.BrushReference.BaseValue; if (!descriptor.MatchesLayerBrushReference(current)) - General.BrushReference.CurrentValue = new LayerBrushReference(descriptor); + General.BrushReference.BaseValue = new LayerBrushReference(descriptor); ActivateLayerBrush(); } diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs index 2e97c6bbb..34635c846 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs @@ -13,7 +13,7 @@ using Stylet; namespace Artemis.UI.Shared.Input { - public class DataModelDynamicViewModel : PropertyChangedBase + public class DataModelDynamicViewModel : PropertyChangedBase, IDisposable { private readonly IDataModelUIService _dataModelUIService; private readonly Module _module; @@ -127,5 +127,12 @@ namespace Artemis.UI.Shared.Input } #endregion + + public void Dispose() + { + _updateTimer.Stop(); + _updateTimer.Dispose(); + _updateTimer.Elapsed -= OnUpdateTimerOnElapsed; + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Modules/ModuleRootViewModel.cs b/src/Artemis.UI/Screens/Modules/ModuleRootViewModel.cs index 93c6985d0..6437a5cd5 100644 --- a/src/Artemis.UI/Screens/Modules/ModuleRootViewModel.cs +++ b/src/Artemis.UI/Screens/Modules/ModuleRootViewModel.cs @@ -29,7 +29,7 @@ namespace Artemis.UI.Screens.Modules AddTabs(); base.OnActivate(); } - + private void AddTabs() { // Create the profile editor and module VMs diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml index a3aaf5c56..48f6007e6 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierView.xaml @@ -18,14 +18,6 @@ - - - - - - - - @@ -62,7 +54,6 @@ Style="{StaticResource DisplayConditionButtonLeftClickMenu}" Background="#7B7B7B" BorderBrush="#7B7B7B" - Content="{Binding SelectedModifierType.Description}" Click="PropertyButton_OnClick"> @@ -83,6 +74,15 @@ + + + + « Select a modifier » + + + diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs index 4ce493458..431185848 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs @@ -1,4 +1,5 @@ -using Artemis.Core; +using System; +using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Exceptions; using Artemis.UI.Shared; @@ -8,7 +9,7 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { - public class DataBindingModifierViewModel : PropertyChangedBase + public class DataBindingModifierViewModel : PropertyChangedBase, IDisposable { private readonly IDataBindingService _dataBindingService; private readonly IDataModelUIService _dataModelUIService; @@ -34,8 +35,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings SelectModifierTypeCommand = new DelegateCommand(ExecuteSelectModifierTypeCommand); - // Initialize async, no need to wait for it - Execute.PostToUIThread(Update); + Update(); } public DelegateCommand SelectModifierTypeCommand { get; } @@ -92,6 +92,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings if (sourceType == null) throw new ArtemisUIException("Cannot use a data binding modifier VM for a data binding without a source"); + if (DynamicSelectionViewModel != null) + { + DynamicSelectionViewModel.Dispose(); + DynamicSelectionViewModel.PropertySelected -= ParameterSelectionViewModelOnPropertySelected; + } + if (Modifier.ModifierType == null || !Modifier.ModifierType.SupportsParameter) { StaticInputViewModel = null; @@ -101,14 +107,22 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { StaticInputViewModel = null; DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); - DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected; - DynamicSelectionViewModel.FilterTypes = new[] {sourceType}; + if (DynamicSelectionViewModel != null) + { + DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected; + DynamicSelectionViewModel.FilterTypes = new[] {sourceType}; + } } else { DynamicSelectionViewModel = null; - StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(sourceType); - StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated; + if (Modifier.ModifierType.PreferredParameterType != null && sourceType.IsCastableFrom(Modifier.ModifierType.PreferredParameterType)) + StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(Modifier.ModifierType.PreferredParameterType); + else + StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(sourceType); + + if (StaticInputViewModel != null) + StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated; } // Modifier type @@ -118,7 +132,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings // Parameter if (DynamicSelectionViewModel != null) - DynamicSelectionViewModel?.PopulateSelectedPropertyViewModel(Modifier.ParameterDataModel, Modifier.ParameterPropertyPath); + DynamicSelectionViewModel.PopulateSelectedPropertyViewModel(Modifier.ParameterDataModel, Modifier.ParameterPropertyPath); else if (StaticInputViewModel != null) StaticInputViewModel.Value = Modifier.ParameterStaticValue; } @@ -133,5 +147,14 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings Update(); } + + public void Dispose() + { + if (DynamicSelectionViewModel != null) + { + DynamicSelectionViewModel.Dispose(); + DynamicSelectionViewModel.PropertySelected -= ParameterSelectionViewModelOnPropertySelected; + } + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml index 779744f82..12554e038 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingView.xaml @@ -126,7 +126,7 @@ FontSize="12" Padding="6 4" Height="22" - IsEnabled="{Binding IsDataBindingEnabled}" + IsEnabled="{Binding CanAddModifier}" Command="{s:Action AddModifier}"> diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs index 0cee22ffa..3ee2cda64 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs @@ -6,6 +6,7 @@ using Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline; using Artemis.UI.Shared; using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Services; +using Ninject.Planning.Targets; using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings @@ -25,6 +26,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings private TProperty _testInputValue; private TProperty _testResultValue; private bool _updating; + private bool _canAddModifier; public DataBindingViewModel(DataBindingRegistration registration, IProfileEditorService profileEditorService, @@ -51,8 +53,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings _isDataBindingEnabled = DataBinding != null; - // Initialize async, no need to wait for it - Execute.PostToUIThread(Initialize); + Initialize(); } public DataBindingRegistration Registration { get; } @@ -116,6 +117,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings } } + public bool CanAddModifier + { + get => _canAddModifier; + private set => SetAndNotify(ref _canAddModifier, value); + } + public DataBinding DataBinding { get => _dataBinding; @@ -169,7 +176,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings if (DataBinding == null) return; - var modifier = new DataBindingModifier(ProfileRightSideType.Dynamic); + var modifier = new DataBindingModifier(DataBinding, ProfileRightSideType.Dynamic); DataBinding.AddModifier(modifier); _profileEditorService.UpdateSelectedProfileElement(); @@ -190,6 +197,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings if (_updating) return; + CanAddModifier = IsDataBindingEnabled && DataBinding.SourceDataModel != null; + if (DataBinding == null) { TargetSelectionViewModel.IsEnabled = false; @@ -218,9 +227,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings if (_updating) return; - DataBinding.Mode = SelectedDataBindingMode; - DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime); - DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear; + if (DataBinding != null) + { + DataBinding.Mode = SelectedDataBindingMode; + DataBinding.EasingTime = TimeSpan.FromMilliseconds(EasingTime); + DataBinding.EasingFunction = SelectedEasingViewModel?.EasingFunction ?? Easings.Functions.Linear; + } _profileEditorService.UpdateSelectedProfileElement(); Update(); @@ -238,8 +250,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings var currentValue = TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue(); if (currentValue == null) currentValue = default(TProperty); - - TestInputValue = (TProperty) Convert.ChangeType(currentValue, typeof(TProperty)); + + TestInputValue = Registration.Converter.ConvertFromObject(currentValue); if (DataBinding != null) TestResultValue = DataBinding.GetValue(TestInputValue); else @@ -248,7 +260,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings private void UpdateModifierViewModels() { + foreach (var dataBindingModifierViewModel in ModifierViewModels) + dataBindingModifierViewModel.Dispose(); ModifierViewModels.Clear(); + if (DataBinding == null) return; @@ -269,6 +284,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) { DataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath); + Update(); + _profileEditorService.UpdateSelectedProfileElement(); } @@ -276,8 +293,14 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { _profileEditorService.ProfilePreviewUpdated -= ProfileEditorServiceOnProfilePreviewUpdated; TargetSelectionViewModel.PropertySelected -= TargetSelectionViewModelOnPropertySelected; + TargetSelectionViewModel.Dispose(); if (DataBinding != null) DataBinding.ModifiersUpdated -= DataBindingOnModifiersUpdated; + + foreach (var dataBindingModifierViewModel in ModifierViewModels) + dataBindingModifierViewModel.Dispose(); + ModifierViewModels.Clear(); + } } diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs index b2a0080bf..b5d6d3a09 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs @@ -52,6 +52,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties public void Dispose() { + TimelineGroupViewModel.Dispose(); LayerPropertyGroup.VisibilityChanged -= LayerPropertyGroupOnVisibilityChanged; foreach (var child in Children) { diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs index 859643892..7f4489171 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineEasingViewModel.cs @@ -3,10 +3,11 @@ using System.Windows; using System.Windows.Media; using Artemis.Core; using Humanizer; +using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline { - public class TimelineEasingViewModel + public class TimelineEasingViewModel : PropertyChangedBase { private bool _isEasingModeSelected; diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs index 60122cc4a..ea75c170b 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs @@ -7,7 +7,7 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline { - public class TimelineGroupViewModel : IDisposable + public class TimelineGroupViewModel : PropertyChangedBase, IDisposable { private readonly IProfileEditorService _profileEditorService; diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs index 6eee624df..1502ac21e 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs @@ -22,6 +22,8 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { _profileEditorService = profileEditorService; _profileTreeVmFactory = profileTreeVmFactory; + + CreateRootFolderViewModel(); } public TreeItemViewModel SelectedTreeItem @@ -94,17 +96,16 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree ActiveItem?.AddLayer(); } - protected override void OnInitialActivate() + protected override void OnActivate() { Subscribe(); - CreateRootFolderViewModel(); - base.OnInitialActivate(); + base.OnActivate(); } - protected override void OnClose() + protected override void OnDeactivate() { Unsubscribe(); - base.OnClose(); + base.OnDeactivate(); } private void CreateRootFolderViewModel() @@ -140,7 +141,7 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree { if (parent == source) return DragDropType.None; - parent = (TreeItemViewModel) parent.Parent; + parent = parent.Parent as TreeItemViewModel; } switch (dropInfo.InsertPosition) diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs index 0c5b0f566..32bc7f957 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/ProfileViewModel.cs @@ -173,8 +173,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization .ToList(); } - protected override void OnInitialActivate() + protected override void OnActivate() { + ApplyActiveProfile(); + OnlyShowSelectedShape = _settingsService.GetSetting("ProfileEditor.OnlyShowSelectedShape", true); HighlightSelectedLayer = _settingsService.GetSetting("ProfileEditor.HighlightSelectedLayer", true); @@ -184,10 +186,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization _profileEditorService.ProfileElementSelected += OnProfileElementSelected; _profileEditorService.SelectedProfileElementUpdated += OnSelectedProfileElementUpdated; - base.OnInitialActivate(); + base.OnActivate(); } - protected override void OnClose() + protected override void OnDeactivate() { HighlightSelectedLayer.SettingChanged -= HighlightSelectedLayerOnSettingChanged; _surfaceService.ActiveSurfaceConfigurationSelected -= OnActiveSurfaceConfigurationSelected; @@ -204,12 +206,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization canvasViewModel.Dispose(); CanvasViewModels.Clear(); - base.OnClose(); - } - - protected override void OnActivate() - { - ApplyActiveProfile(); + base.OnDeactivate(); } private void OnActiveSurfaceConfigurationSelected(object sender, SurfaceConfigurationEventArgs e) diff --git a/src/Artemis.UI/Screens/RootViewModel.cs b/src/Artemis.UI/Screens/RootViewModel.cs index 448a7d04b..0f551b77c 100644 --- a/src/Artemis.UI/Screens/RootViewModel.cs +++ b/src/Artemis.UI/Screens/RootViewModel.cs @@ -247,13 +247,14 @@ namespace Artemis.UI.Screens _colorScheme.SettingChanged -= ColorSchemeOnSettingChanged; _themeWatcher.ThemeChanged -= ThemeWatcherOnThemeChanged; SidebarViewModel.PropertyChanged -= SidebarViewModelOnPropertyChanged; + + base.OnDeactivate(); } protected override void OnClose() { SidebarViewModel.Dispose(); - - + // Lets force the GC to run after closing the window so it is obvious to users watching task manager // that closing the UI will decrease the memory footprint of the application. Task.Run(async () => @@ -263,6 +264,8 @@ namespace Artemis.UI.Screens GC.WaitForPendingFinalizers(); GC.Collect(); }); + + base.OnClose(); } #endregion