diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index 5e96cd8b1..1da8ef7dc 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -1,12 +1,8 @@ using Artemis.Core.Models.Profile; -using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.Abstract; using Artemis.UI.Screens.Module; using Artemis.UI.Screens.Module.ProfileEditor; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline; using Artemis.UI.Screens.Module.ProfileEditor.ProfileTree.TreeItem; using Artemis.UI.Screens.Module.ProfileEditor.Visualization; using Artemis.UI.Screens.Settings.Tabs.Devices; diff --git a/src/Artemis.UI/Ninject/UiModule.cs b/src/Artemis.UI/Ninject/UiModule.cs index 6f9550f7b..6280c051a 100644 --- a/src/Artemis.UI/Ninject/UiModule.cs +++ b/src/Artemis.UI/Ninject/UiModule.cs @@ -2,7 +2,6 @@ using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens; using Artemis.UI.Screens.Module.ProfileEditor; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput; using Artemis.UI.Services.Interfaces; using Artemis.UI.Shared.Services.Dialog; using Artemis.UI.Stylet; @@ -57,16 +56,7 @@ namespace Artemis.UI.Ninject .InheritedFrom() .BindAllBaseClasses(); }); - - // Bind property input VMs - Kernel.Bind(x => - { - x.FromThisAssembly() - .SelectAllClasses() - .InheritedFrom() - .BindAllBaseClasses(); - }); - + // Bind all UI services as singletons Kernel.Bind(x => { diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/Abstract/PropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/Abstract/PropertyInputViewModel.cs new file mode 100644 index 000000000..9a529a886 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/Abstract/PropertyInputViewModel.cs @@ -0,0 +1,93 @@ +using System; +using Stylet; + +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract +{ + public abstract class PropertyInputViewModel : ValidatingModelBase, IDisposable + { + private T _inputValue; + + protected PropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel) + { + LayerPropertyViewModel = layerPropertyViewModel; + LayerPropertyViewModel.LayerProperty.Updated += LayerPropertyOnUpdated; + } + + protected PropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel, IModelValidator validator) : base(validator) + { + LayerPropertyViewModel = layerPropertyViewModel; + LayerPropertyViewModel.LayerProperty.Updated += LayerPropertyOnUpdated; + } + + public LayerPropertyViewModel LayerPropertyViewModel { get; } + + public bool InputDragging { get; private set; } + + public T InputValue + { + get => _inputValue; + set + { + _inputValue = value; + ApplyInputValue(); + } + } + + public virtual void Dispose() + { + LayerPropertyViewModel.LayerProperty.Updated -= LayerPropertyOnUpdated; + } + + protected virtual void OnInputValueChanged() + { + } + + private void LayerPropertyOnUpdated(object sender, EventArgs e) + { + UpdateInputValue(); + } + + private void UpdateInputValue() + { + // Avoid unnecessary UI updates and validator cycles + if (_inputValue.Equals(LayerPropertyViewModel.LayerProperty.CurrentValue)) + return; + + // Override the input value + _inputValue = LayerPropertyViewModel.LayerProperty.CurrentValue; + + // Notify a change in the input value + OnInputValueChanged(); + NotifyOfPropertyChange(nameof(InputValue)); + + // Force the validator to run with the overridden value + Validate(); + } + + private void ApplyInputValue() + { + // Force the validator to run + Validate(); + // Only apply the input value to the layer property if the validator found no errors + if (!HasErrors) + LayerPropertyViewModel.SetCurrentValue(_inputValue, !InputDragging); + + OnInputValueChanged(); + } + + #region Event handlers + + public void InputDragStarted(object sender, EventArgs e) + { + InputDragging = true; + } + + public void InputDragEnded(object sender, EventArgs e) + { + InputDragging = false; + LayerPropertyViewModel.ProfileEditorService.UpdateSelectedProfileElement(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputView.xaml index 143a229bf..144bc1a21 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputView.xaml @@ -1,23 +1,25 @@ - + d:DesignHeight="450" d:DesignWidth="800" + d:DataContext="{d:DesignInstance {x:Type propertyInput:BrushPropertyInputViewModel}}"> - + - + SelectedValue="{Binding Path=InputValue}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputViewModel.cs index 2eb194bb1..63284ebcf 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/BrushPropertyInputViewModel.cs @@ -1,16 +1,14 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Artemis.Core.Events; using Artemis.Core.Models.Profile; using Artemis.Core.Plugins.LayerBrush; using Artemis.Core.Services.Interfaces; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; using Artemis.UI.Shared.Utilities; using Stylet; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { public class BrushPropertyInputViewModel : PropertyInputViewModel { @@ -22,59 +20,40 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P { _layerService = layerService; _pluginService = pluginService; - EnumValues = new BindableCollection(); + ComboboxValues = new BindableCollection(); _pluginService.PluginLoaded += PluginServiceOnPluginLoaded; + UpdateEnumValues(); } - public BindableCollection EnumValues { get; } + public BindableCollection ComboboxValues { get; } - public LayerBrushReference BrushInputValue - { - get => (LayerBrushReference) InputValue; - set - { - InputValue = value; - _layerService.InstantiateLayerBrush(LayerPropertyViewModel.LayerProperty.Layer); - } - } - public void UpdateEnumValues() { var layerBrushProviders = _pluginService.GetPluginsOfType(); var descriptors = layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors).ToList(); - + var enumValues = new List(); foreach (var layerBrushDescriptor in descriptors) { var brushName = layerBrushDescriptor.LayerBrushType.Name; var brushGuid = layerBrushDescriptor.LayerBrushProvider.PluginInfo.Guid; - if (BrushInputValue != null && BrushInputValue.BrushType == brushName && BrushInputValue.BrushPluginGuid == brushGuid) - enumValues.Add(new ValueDescription {Description = layerBrushDescriptor.DisplayName, Value = BrushInputValue}); + if (InputValue != null && InputValue.BrushType == brushName && InputValue.BrushPluginGuid == brushGuid) + enumValues.Add(new ValueDescription {Description = layerBrushDescriptor.DisplayName, Value = InputValue}); else enumValues.Add(new ValueDescription {Description = layerBrushDescriptor.DisplayName, Value = new LayerBrushReference {BrushType = brushName, BrushPluginGuid = brushGuid}}); } - EnumValues.Clear(); - EnumValues.AddRange(enumValues); - } - public override void Update() - { - NotifyOfPropertyChange(() => BrushInputValue); + ComboboxValues.Clear(); + ComboboxValues.AddRange(enumValues); } - + public override void Dispose() { _pluginService.PluginLoaded -= PluginServiceOnPluginLoaded; base.Dispose(); } - protected override void OnInitialized() - { - UpdateEnumValues(); - base.OnInitialized(); - } - private void PluginServiceOnPluginLoaded(object sender, PluginEventArgs e) { UpdateEnumValues(); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputView.xaml index c871a6cbe..981433336 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputView.xaml @@ -1,22 +1,20 @@ - + d:DataContext="{d:DesignInstance propertyInput:ColorGradientPropertyInputViewModel}"> - + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputViewModel.cs index beeb353c7..e20bbd7b2 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/ColorGradientPropertyInputViewModel.cs @@ -1,28 +1,12 @@ -using System; -using System.Collections.Generic; -using Artemis.Core.Models.Profile; -using Artemis.Core.Models.Profile.Colors; -using Artemis.UI.Services.Interfaces; +using Artemis.Core.Models.Profile.Colors; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class ColorGradientPropertyInputViewModel : PropertyInputViewModel + public class ColorGradientPropertyInputViewModel : PropertyInputViewModel { - public ColorGradientPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public ColorGradientPropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel) : base(layerPropertyViewModel) { } - - public sealed override List CompatibleTypes { get; } = new List {typeof(ColorGradient)}; - - public ColorGradient ColorGradientInputValue - { - get => (ColorGradient) InputValue ?? new ColorGradient(); - set => InputValue = value; - } - - public override void Update() - { - NotifyOfPropertyChange(() => ColorGradientInputValue); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputView.xaml index 7426a53d8..1d5d36fe4 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputView.xaml @@ -1,15 +1,13 @@ - + d:DesignHeight="450" d:DesignWidth="800"> - + - + SelectedValue="{Binding Path=InputValue}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputViewModel.cs index 2134ab5ea..57ccfcd44 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/EnumPropertyInputViewModel.cs @@ -1,41 +1,17 @@ using System; using System.Collections.Generic; -using System.Linq; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; using Artemis.UI.Shared.Utilities; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class EnumPropertyInputViewModel : PropertyInputViewModel + public class EnumPropertyInputViewModel : PropertyInputViewModel where T : Enum { - public EnumPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public EnumPropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel) : base(layerPropertyViewModel) { + EnumValues = EnumUtilities.GetAllValuesAndDescriptions(typeof(T)); } - - public IEnumerable EnumValues { get; private set; } - - public sealed override List CompatibleTypes { get; } = new List {typeof(Enum)}; - - public object EnumInputValue - { - get => InputValue ?? Enum.GetValues(GetLayerPropertyEnumType()).Cast().First(); - set => InputValue = value; - } - - public override void Update() - { - NotifyOfPropertyChange(() => EnumInputValue); - } - - protected override void OnInitialized() - { - EnumValues = EnumUtilities.GetAllValuesAndDescriptions(GetLayerPropertyEnumType()); - } - - - private Type GetLayerPropertyEnumType() - { - return LayerPropertyViewModel.BaseLayerProperty.GetType().GetGenericTypeDefinition(); - } + + public IEnumerable EnumValues { get; } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputView.xaml index 3a718dfcb..6f9807ade 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputView.xaml @@ -1,21 +1,22 @@ - + d:DataContext="{d:DesignInstance propertyInput:FloatPropertyInputViewModel}"> - - + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputViewModel.cs index 7fabc6bb2..55954397d 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/FloatPropertyInputViewModel.cs @@ -1,39 +1,28 @@ -using System; -using System.Collections.Generic; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; +using FluentValidation; +using Stylet; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class FloatPropertyInputViewModel : PropertyInputViewModel + public class FloatPropertyInputViewModel : PropertyInputViewModel { - public FloatPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public FloatPropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel, IModelValidator validator) + : base(layerPropertyViewModel, validator) { } + } - public sealed override List CompatibleTypes { get; } = new List {typeof(float)}; - - - public float FloatInputValue + public class FloatPropertyInputViewModelValidator : AbstractValidator + { + public FloatPropertyInputViewModelValidator() { - get => (float?) InputValue ?? 0f; - set => InputValue = ApplyInputValue(value); - } + RuleFor(vm => vm.InputValue) + .LessThanOrEqualTo(vm => (float) vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is float); - public override void Update() - { - NotifyOfPropertyChange(() => FloatInputValue); - } - - private float ApplyInputValue(float value) - { - if (LayerPropertyViewModel.LayerProperty.MaxInputValue != null && - LayerPropertyViewModel.LayerProperty.MaxInputValue is float maxFloat) - value = Math.Min(value, maxFloat); - if (LayerPropertyViewModel.LayerProperty.MinInputValue != null && - LayerPropertyViewModel.LayerProperty.MinInputValue is float minFloat) - value = Math.Max(value, minFloat); - - return value; + RuleFor(vm => vm.InputValue) + .GreaterThanOrEqualTo(vm => (float) vm.LayerPropertyViewModel.PropertyDescription.MinInputValue) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MinInputValue is float); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputView.xaml index cf2997f4a..25115253d 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputView.xaml @@ -1,21 +1,22 @@ - + d:DataContext="{d:DesignInstance propertyInput:IntPropertyInputViewModel}"> - - - + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputViewModel.cs index ff2c45302..adce93b8b 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/IntPropertyInputViewModel.cs @@ -1,38 +1,28 @@ -using System; -using System.Collections.Generic; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; +using FluentValidation; +using Stylet; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class IntPropertyInputViewModel : PropertyInputViewModel + public class IntPropertyInputViewModel : PropertyInputViewModel { - public IntPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public IntPropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel, IModelValidator validator) + : base(layerPropertyViewModel, validator) { } + } - public sealed override List CompatibleTypes { get; } = new List {typeof(int)}; - - public int IntInputValue + public class IntPropertyInputViewModelValidator : AbstractValidator + { + public IntPropertyInputViewModelValidator() { - get => (int?) InputValue ?? 0; - set => InputValue = ApplyInputValue(value); - } + RuleFor(vm => vm.InputValue) + .LessThanOrEqualTo(vm => (int) vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is int); - public override void Update() - { - NotifyOfPropertyChange(() => IntInputValue); - } - - private int ApplyInputValue(int value) - { - if (LayerPropertyViewModel.LayerProperty.MaxInputValue != null && - LayerPropertyViewModel.LayerProperty.MaxInputValue is int maxInt) - value = Math.Min(value, maxInt); - if (LayerPropertyViewModel.LayerProperty.MinInputValue != null && - LayerPropertyViewModel.LayerProperty.MinInputValue is int minInt) - value = Math.Max(value, minInt); - - return value; + RuleFor(vm => vm.InputValue) + .GreaterThanOrEqualTo(vm => (int) vm.LayerPropertyViewModel.PropertyDescription.MinInputValue) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MinInputValue is int); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/PropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/PropertyInputViewModel.cs deleted file mode 100644 index b24885717..000000000 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/PropertyInputViewModel.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Stylet; - -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput -{ - public abstract class PropertyInputViewModel : PropertyChangedBase, IDisposable - { - protected PropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel) - { - LayerPropertyViewModel = layerPropertyViewModel; - LayerPropertyViewModel.LayerProperty.Updated += LayerPropertyOnUpdated; - } - - public LayerPropertyViewModel LayerPropertyViewModel { get; } - - public bool InputDragging { get; private set; } - - protected T InputValue - { - get => LayerPropertyViewModel.LayerProperty.CurrentValue; - set => LayerPropertyViewModel.SetCurrentValue(value, !InputDragging); - } - - public abstract void Update(); - - private void LayerPropertyOnUpdated(object? sender, EventArgs e) - { - Update(); - } - - #region Event handlers - - public void InputDragStarted(object sender, EventArgs e) - { - InputDragging = true; - } - - public void InputDragEnded(object sender, EventArgs e) - { - InputDragging = false; - LayerPropertyViewModel.ProfileEditorService.UpdateSelectedProfileElement(); - } - - #endregion - - public void Dispose() - { - LayerPropertyViewModel.LayerProperty.Updated -= LayerPropertyOnUpdated; - } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputView.xaml index 524991b5e..691cf7544 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputView.xaml @@ -1,24 +1,25 @@ - + d:DataContext="{d:DesignInstance propertyInput:SKColorPropertyInputViewModel}"> - + - + Color="{Binding InputValue, Converter={StaticResource SKColorToColorConverter}}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputViewModel.cs index ed5ca0332..8e28eaeb2 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKColorPropertyInputViewModel.cs @@ -1,27 +1,12 @@ -using System; -using System.Collections.Generic; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; using SkiaSharp; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class SKColorPropertyInputViewModel : PropertyInputViewModel + public class SKColorPropertyInputViewModel : PropertyInputViewModel { - public SKColorPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public SKColorPropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel) : base(layerPropertyViewModel) { } - - public sealed override List CompatibleTypes { get; } = new List {typeof(SKColor)}; - - public SKColor SKColorInputValue - { - get => (SKColor?) InputValue ?? new SKColor(); - set => InputValue = value; - } - - public override void Update() - { - NotifyOfPropertyChange(() => SKColorInputValue); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputView.xaml index bf0243f20..e4c49615d 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputView.xaml @@ -1,28 +1,29 @@ - + d:DataContext="{d:DesignInstance propertyInput:SKPointPropertyInputViewModel}"> - + , - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputViewModel.cs index 15be6cecc..894b0d338 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKPointPropertyInputViewModel.cs @@ -1,50 +1,56 @@ -using System; -using System.Collections.Generic; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; +using FluentValidation; using PropertyChanged; using SkiaSharp; +using Stylet; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class SKPointPropertyInputViewModel : PropertyInputViewModel + public class SKPointPropertyInputViewModel : PropertyInputViewModel { - public SKPointPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public SKPointPropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel, IModelValidator validator) : base(layerPropertyViewModel, validator) { } - public sealed override List CompatibleTypes { get; } = new List {typeof(SKPoint)}; - // Since SKPoint is immutable we need to create properties that replace the SKPoint entirely [DependsOn(nameof(InputValue))] public float X { - get => ((SKPoint?) InputValue)?.X ?? 0; - set => InputValue = new SKPoint(ApplyInputValue(value), Y); + get => InputValue.X; + set => InputValue = new SKPoint(value, Y); } [DependsOn(nameof(InputValue))] public float Y { - get => ((SKPoint?) InputValue)?.Y ?? 0; - set => InputValue = new SKPoint(X, ApplyInputValue(value)); + get => InputValue.Y; + set => InputValue = new SKPoint(X, value); } - public override void Update() + protected override void OnInputValueChanged() { - NotifyOfPropertyChange(() => X); - NotifyOfPropertyChange(() => Y); + NotifyOfPropertyChange(nameof(X)); + NotifyOfPropertyChange(nameof(Y)); } + } - private float ApplyInputValue(float value) + public class SKPointPropertyInputViewModelValidator : AbstractValidator + { + public SKPointPropertyInputViewModelValidator() { - if (LayerPropertyViewModel.LayerProperty.MaxInputValue != null && - LayerPropertyViewModel.LayerProperty.MaxInputValue is float maxFloat) - value = Math.Min(value, maxFloat); - if (LayerPropertyViewModel.LayerProperty.MinInputValue != null && - LayerPropertyViewModel.LayerProperty.MinInputValue is float minFloat) - value = Math.Max(value, minFloat); + RuleFor(vm => vm.X) + .LessThanOrEqualTo(vm => ((SKPoint) vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).X) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKPoint); + RuleFor(vm => vm.X) + .GreaterThanOrEqualTo(vm => ((SKPoint) vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).X) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKPoint); - return value; + RuleFor(vm => vm.Y) + .LessThanOrEqualTo(vm => ((SKPoint) vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).Y) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKPoint); + RuleFor(vm => vm.Y) + .GreaterThanOrEqualTo(vm => ((SKPoint) vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).Y) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKPoint); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputView.xaml index 548616776..d581fa6a9 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputView.xaml @@ -1,9 +1,9 @@ - - + , - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputViewModel.cs index 7b9e3e204..5f5bb2093 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/PropertyInput/SKSizePropertyInputViewModel.cs @@ -1,50 +1,56 @@ -using System; -using System.Collections.Generic; -using Artemis.UI.Services.Interfaces; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; +using FluentValidation; using PropertyChanged; using SkiaSharp; +using Stylet; -namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput { - public class SKSizePropertyInputViewModel : PropertyInputViewModel + public class SKSizePropertyInputViewModel : PropertyInputViewModel { - public SKSizePropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + public SKSizePropertyInputViewModel(LayerPropertyViewModel layerPropertyViewModel, IModelValidator validator) : base(layerPropertyViewModel, validator) { } - public sealed override List CompatibleTypes { get; } = new List {typeof(SKSize)}; - - // Since SKSize is immutable we need to create properties that replace the SKPoint entirely + // Since SKSize is immutable we need to create properties that replace the SKSize entirely [DependsOn(nameof(InputValue))] public float Width { - get => ((SKSize?) InputValue)?.Width ?? 0; - set => InputValue = new SKSize(ApplyInputValue(value), Height); + get => InputValue.Width; + set => InputValue = new SKSize(value, Height); } [DependsOn(nameof(InputValue))] public float Height { - get => ((SKSize?) InputValue)?.Height ?? 0; - set => InputValue = new SKSize(Width, ApplyInputValue(value)); + get => InputValue.Height; + set => InputValue = new SKSize(Width, value); } - public override void Update() + protected override void OnInputValueChanged() { - NotifyOfPropertyChange(() => Width); - NotifyOfPropertyChange(() => Height); + NotifyOfPropertyChange(nameof(Width)); + NotifyOfPropertyChange(nameof(Height)); } + } - private float ApplyInputValue(float value) + public class SKSizePropertyInputViewModelValidator : AbstractValidator + { + public SKSizePropertyInputViewModelValidator() { - if (LayerPropertyViewModel.LayerProperty.MaxInputValue != null && - LayerPropertyViewModel.LayerProperty.MaxInputValue is float maxFloat) - value = Math.Min(value, maxFloat); - if (LayerPropertyViewModel.LayerProperty.MinInputValue != null && - LayerPropertyViewModel.LayerProperty.MinInputValue is float minFloat) - value = Math.Max(value, minFloat); + RuleFor(vm => vm.Width) + .LessThanOrEqualTo(vm => ((SKSize)vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).Width) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKSize); + RuleFor(vm => vm.Width) + .GreaterThanOrEqualTo(vm => ((SKSize)vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).Width) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKSize); - return value; + RuleFor(vm => vm.Height) + .LessThanOrEqualTo(vm => ((SKSize)vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).Height) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKSize); + RuleFor(vm => vm.Height) + .GreaterThanOrEqualTo(vm => ((SKSize)vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue).Height) + .When(vm => vm.LayerPropertyViewModel.PropertyDescription.MaxInputValue is SKSize); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs index fc791154a..d670a3789 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyViewModel.cs @@ -1,20 +1,15 @@ using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using Artemis.Core.Models.Profile; -using Artemis.UI.Exceptions; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput; -using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree.PropertyInput.Abstract; namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree { public class TreePropertyViewModel : TreePropertyViewModel { - public TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel) : base(layerPropertyBaseViewModel) + public TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel, PropertyInputViewModel propertyInputViewModel) : base(layerPropertyBaseViewModel) { LayerPropertyViewModel = (LayerPropertyViewModel) layerPropertyBaseViewModel; - PropertyInputViewModel = CreatePropertyInputViewModel(); + PropertyInputViewModel = propertyInputViewModel; } public LayerPropertyViewModel LayerPropertyViewModel { get; } @@ -24,36 +19,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree { PropertyInputViewModel.Dispose(); } - - private PropertyInputViewModel CreatePropertyInputViewModel() - { - if (!IsPropertySupported(typeof(T))) - throw new ArtemisUIException($"Failed to create a property input view model, type {typeof(T).Name} is not supported."); - - return (PropertyInputViewModel) Activator.CreateInstance(SupportedTypes[typeof(T)], this); - } } public abstract class TreePropertyViewModel : IDisposable { - public static ReadOnlyDictionary SupportedTypes = new ReadOnlyDictionary(new Dictionary - { - {typeof(LayerBrushReference), typeof(BrushPropertyInputViewModel)}, - {typeof(LayerBrushReference), typeof(BrushPropertyInputViewModel)}, - {typeof(LayerBrushReference), typeof(BrushPropertyInputViewModel)}, - {typeof(LayerBrushReference), typeof(BrushPropertyInputViewModel)} - }); - - public static void RegisterPropertyInputViewModel() - { - - } - - public static bool IsPropertySupported(Type type) - { - return SupportedTypes.ContainsKey(type); - } - protected TreePropertyViewModel(LayerPropertyBaseViewModel layerPropertyBaseViewModel) { LayerPropertyBaseViewModel = layerPropertyBaseViewModel; @@ -61,5 +30,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree public LayerPropertyBaseViewModel LayerPropertyBaseViewModel { get; } public abstract void Dispose(); + + public static void RegisterPropertyInputViewModel() + { + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs index 585276011..b6ec65f93 100644 --- a/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs +++ b/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs @@ -5,6 +5,7 @@ using Artemis.Core.Models.Profile.LayerProperties.Attributes; using Artemis.Core.Plugins.Abstract; using Artemis.UI.Events; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; +using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree; namespace Artemis.UI.Services.Interfaces { @@ -54,5 +55,7 @@ namespace Artemis.UI.Services.Interfaces /// Occurs when the profile preview has been updated /// event EventHandler ProfilePreviewUpdated; + + TreePropertyViewModel CreateTreePropertyViewModel(); } } \ No newline at end of file