From db9d9fb4e6226fde4ae1e182e9cca6d4c8c7da8d Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Fri, 11 Sep 2020 22:30:57 +0200 Subject: [PATCH] Profile editor - Reimplemented more of the timeline --- src/Artemis.Core/Models/Profile/Layer.cs | 10 ++- .../Profile/LayerProperties/LayerProperty.cs | 22 +++---- .../Input/DataModelDynamicViewModel.cs | 5 +- .../Services/DataModelUIService.cs | 26 +++++--- .../Interfaces/IDataModelUIService.cs | 9 +-- .../Ninject/Factories/IVMFactory.cs | 10 +-- .../BrushPropertyInputViewModel.cs | 14 ++--- .../DisplayConditionListPredicateViewModel.cs | 5 +- .../DisplayConditionListViewModel.cs | 5 +- .../DisplayConditionPredicateViewModel.cs | 11 +--- .../DataBindingModifierViewModel.cs | 46 +++++++------- .../DataBindings/DataBindingViewModel.cs | 40 +++++++----- .../DataBindings/DataBindingsTabsView.xaml | 16 ----- .../DataBindings/DataBindingsTabsViewModel.cs | 14 ----- .../DataBindings/DataBindingsView.xaml | 16 ++--- .../DataBindings/DataBindingsViewModel.cs | 45 +++----------- .../LayerPropertiesViewModel.cs | 52 ++++++++-------- .../LayerPropertyGroupViewModel.cs | 41 ++++++++++-- .../Timeline/TimelineGroupViewModel.cs | 4 +- .../Timeline/TimelineKeyframeViewModel.cs | 62 +++++++++++++++---- .../Timeline/TimelinePropertyViewModel.cs | 32 +++++++++- .../Timeline/TimelineSegmentViewModel.cs | 36 +++++++---- .../Timeline/TimelineViewModel.cs | 59 +++++------------- .../Tools/SelectionToolViewModel.cs | 10 +-- 24 files changed, 294 insertions(+), 296 deletions(-) delete mode 100644 src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsView.xaml delete mode 100644 src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsViewModel.cs diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index fc8dbb7c6..1cd1e5445 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; using Artemis.Core.LayerBrushes; using Artemis.Core.LayerEffects; @@ -672,6 +673,13 @@ namespace Artemis.Core if (descriptor == null) throw new ArgumentNullException(nameof(descriptor)); + if (LayerBrush != null) + { + var brush = LayerBrush; + LayerBrush = null; + brush.Dispose(); + } + // Ensure the brush reference matches the brush var current = General.BrushReference.CurrentValue; if (current.BrushPluginGuid != descriptor.LayerBrushProvider.PluginInfo.Guid || current.BrushType != descriptor.LayerBrushType.Name) @@ -721,7 +729,7 @@ namespace Artemis.Core } #endregion - + #region Event handlers private void LayerBrushStoreOnLayerBrushRemoved(object sender, LayerBrushStoreEvent e) diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index ed49633ad..339401608 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -19,7 +19,7 @@ namespace Artemis.Core public abstract class LayerProperty : ILayerProperty { private bool _disposed; - + /// /// Creates a new instance of the class /// @@ -274,19 +274,6 @@ namespace Artemis.Core OnKeyframeRemoved(); } - /// - /// Removes all keyframes from the layer property - /// - public void ClearKeyframes() - { - if (_disposed) - throw new ObjectDisposedException("LayerProperty"); - - var keyframes = new List>(_keyframes); - foreach (var layerPropertyKeyframe in keyframes) - RemoveKeyframe(layerPropertyKeyframe); - } - /// /// Sorts the keyframes in ascending order by position /// @@ -343,6 +330,11 @@ namespace Artemis.Core return (DataBindingRegistration) match; } + public List GetAllDataBindingRegistrations() + { + return _dataBindingRegistrations; + } + public void RegisterDataBindingProperty(Expression> propertyLambda, DataBindingConverter converter) { if (_disposed) @@ -593,7 +585,7 @@ namespace Artemis.Core { _disposed = true; - foreach (var dataBinding in _dataBindings) + foreach (var dataBinding in _dataBindings) dataBinding.Dispose(); } } diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs index bc718153d..2e97c6bbb 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicViewModel.cs @@ -89,10 +89,7 @@ namespace Artemis.UI.Shared.Input private void Initialize() { // Get the data models - DataModelViewModel = _dataModelUIService.GetMainDataModelVisualization(); - if (!_dataModelUIService.GetPluginExtendsDataModel(_module)) - DataModelViewModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_module)); - + DataModelViewModel = _dataModelUIService.GetPluginDataModelVisualization(_module, true); DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested; _updateTimer.Start(); diff --git a/src/Artemis.UI.Shared/Services/DataModelUIService.cs b/src/Artemis.UI.Shared/Services/DataModelUIService.cs index 73e34e9e6..280bc4b5f 100644 --- a/src/Artemis.UI.Shared/Services/DataModelUIService.cs +++ b/src/Artemis.UI.Shared/Services/DataModelUIService.cs @@ -32,7 +32,7 @@ namespace Artemis.UI.Shared.Services public DataModelPropertiesViewModel GetMainDataModelVisualization() { var viewModel = new DataModelPropertiesViewModel(null, null, null); - foreach (var dataModelExpansion in _dataModelService.DataModelExpansions) + foreach (var dataModelExpansion in _dataModelService.GetDataModels()) viewModel.Children.Add(new DataModelPropertiesViewModel(dataModelExpansion, viewModel, null)); // Update to populate children @@ -41,8 +41,23 @@ namespace Artemis.UI.Shared.Services return viewModel; } - public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin) + public DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool includeMainDataModel) { + if (includeMainDataModel) + { + var mainDataModel = GetMainDataModelVisualization(); + + // If the main data model already includes the plugin data model we're done + if (mainDataModel.Children.Any(c => c.DataModel.PluginInfo.Instance == plugin)) + return mainDataModel; + // Otherwise get just the plugin data model and add it + var pluginDataModel = GetPluginDataModelVisualization(plugin, false); + if (pluginDataModel != null) + mainDataModel.Children.Add(pluginDataModel); + + return mainDataModel; + } + var dataModel = _dataModelService.GetPluginDataModel(plugin); if (dataModel == null) return null; @@ -55,12 +70,7 @@ namespace Artemis.UI.Shared.Services viewModel.UpdateRequested += (sender, args) => viewModel.Update(this); return viewModel; } - - public bool GetPluginExtendsDataModel(Plugin plugin) - { - return _dataModelService.GetPluginExtendsDataModel(plugin); - } - + public DataModelVisualizationRegistration RegisterDataModelInput(PluginInfo pluginInfo, IReadOnlyCollection compatibleConversionTypes = null) where T : DataModelInputViewModel { if (compatibleConversionTypes == null) diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs index 049a1bf11..f9cb711df 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IDataModelUIService.cs @@ -12,14 +12,7 @@ namespace Artemis.UI.Shared.Services IReadOnlyCollection RegisteredDataModelEditors { get; } IReadOnlyCollection RegisteredDataModelDisplays { get; } DataModelPropertiesViewModel GetMainDataModelVisualization(); - DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin); - - /// - /// Determines whether the given plugin expands the main data model - /// - /// - /// - bool GetPluginExtendsDataModel(Plugin plugin); + DataModelPropertiesViewModel GetPluginDataModelVisualization(Plugin plugin, bool includeMainDataModel); DataModelVisualizationRegistration RegisterDataModelInput(PluginInfo pluginInfo, IReadOnlyCollection compatibleConversionTypes) where T : DataModelInputViewModel; DataModelVisualizationRegistration RegisterDataModelDisplay(PluginInfo pluginInfo) where T : DataModelDisplayViewModel; diff --git a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs index 86b82e29e..ef45ad046 100644 --- a/src/Artemis.UI/Ninject/Factories/IVMFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IVMFactory.cs @@ -71,21 +71,21 @@ namespace Artemis.UI.Ninject.Factories public interface IDataBindingsVmFactory : IVmFactory { - DataBindingsViewModel DataBindingsViewModel(BaseLayerProperty layerProperty); - DataBindingViewModel DataBindingViewModel(DataBindingRegistration registration); - DataBindingModifierViewModel DataBindingModifierViewModel(DataBindingModifier modifier); + IDataBindingViewModel DataBindingViewModel(IDataBindingRegistration registration); + DataBindingModifierViewModel DataBindingModifierViewModel(DataBindingModifier modifier); } public interface ILayerPropertyVmFactory : IVmFactory { LayerPropertyViewModel LayerPropertyViewModel(ILayerProperty layerProperty); + LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup); - TreePropertyViewModel LayerPropertyGroupViewModel(LayerProperty layerProperty); TreeGroupViewModel TreeGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel); TimelineGroupViewModel TimelineGroupViewModel(LayerPropertyGroupViewModel layerPropertyGroupViewModel); - LayerPropertyGroupViewModel LayerPropertyGroupViewModel(LayerPropertyGroup layerPropertyGroup, PropertyGroupDescriptionAttribute propertyGroupDescription); + TreeViewModel TreeViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups); EffectsViewModel EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel); TimelineViewModel TimelineViewModel(LayerPropertiesViewModel layerPropertiesViewModel, BindableCollection layerPropertyGroups); + TimelineSegmentViewModel TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection layerPropertyGroups); } } \ No newline at end of file diff --git a/src/Artemis.UI/PropertyInput/BrushPropertyInputViewModel.cs b/src/Artemis.UI/PropertyInput/BrushPropertyInputViewModel.cs index a51d405fb..2222ecf81 100644 --- a/src/Artemis.UI/PropertyInput/BrushPropertyInputViewModel.cs +++ b/src/Artemis.UI/PropertyInput/BrushPropertyInputViewModel.cs @@ -11,13 +11,12 @@ namespace Artemis.UI.PropertyInput public class BrushPropertyInputViewModel : PropertyInputViewModel { private readonly IPluginService _pluginService; - private readonly IRenderElementService _renderElementService; private List _descriptors; - public BrushPropertyInputViewModel(LayerProperty layerProperty, IProfileEditorService profileEditorService, - IRenderElementService renderElementService, IPluginService pluginService) : base(layerProperty, profileEditorService) + public BrushPropertyInputViewModel(LayerProperty layerProperty, + IProfileEditorService profileEditorService, + IPluginService pluginService) : base(layerProperty, profileEditorService) { - _renderElementService = renderElementService; _pluginService = pluginService; _pluginService.PluginEnabled += PluginServiceOnPluginLoaded; @@ -54,11 +53,8 @@ namespace Artemis.UI.PropertyInput protected override void OnInputValueApplied() { - if (LayerProperty.ProfileElement is Layer layer) - { - _renderElementService.RemoveLayerBrush(layer); - _renderElementService.InstantiateLayerBrush(layer); - } + if (LayerProperty.ProfileElement is Layer layer) + layer.ChangeLayerBrush(SelectedDescriptor); } private void SetBrushByDescriptor(LayerBrushDescriptor value) diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs index 7d07d3c11..07738d392 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs @@ -308,10 +308,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions if (DisplayConditionListPredicate.ListDataModel == null || DisplayConditionListPredicate.ListPropertyPath == null) throw new ArtemisUIException("Cannot create a list predicate without first selecting a target list"); - var dataModel = _dataModelUIService.GetMainDataModelVisualization(); - if (!_dataModelUIService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule())) - dataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule())); - + var dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); var listDataModel = (DataModelListViewModel) dataModel.GetChildByPath( DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid, DisplayConditionListPredicate.ListPropertyPath diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs index 73393f6cb..f627d87bc 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs @@ -109,10 +109,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions public void Initialize() { // Get the data models - TargetDataModel = _dataModelUIService.GetMainDataModelVisualization(); - if (!_dataModelUIService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule())) - TargetDataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule())); - + TargetDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); TargetDataModel.UpdateRequested += TargetDataModelUpdateRequested; Update(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs index f8b7833bd..645237a34 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs @@ -168,14 +168,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions public void Initialize() { // Get the data models - LeftSideDataModel = _dataModelUIService.GetMainDataModelVisualization(); - RightSideDataModel = _dataModelUIService.GetMainDataModelVisualization(); - if (!_dataModelUIService.GetPluginExtendsDataModel(_profileEditorService.GetCurrentModule())) - { - LeftSideDataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule())); - RightSideDataModel.Children.Add(_dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule())); - } - + LeftSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); + RightSideDataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true); + // Determine which types are currently supported var editors = _dataModelUIService.RegisteredDataModelEditors; _supportedInputTypes = editors.Select(e => e.SupportedType).ToList(); diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs index 58849824e..4ce493458 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingModifierViewModel.cs @@ -1,6 +1,4 @@ -using System; -using System.Threading.Tasks; -using Artemis.Core; +using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Exceptions; using Artemis.UI.Shared; @@ -10,16 +8,16 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { - public class DataBindingModifierViewModel : PropertyChangedBase + public class DataBindingModifierViewModel : PropertyChangedBase { private readonly IDataBindingService _dataBindingService; private readonly IDataModelUIService _dataModelUIService; private readonly IProfileEditorService _profileEditorService; - private DataBindingModifierType _selectedModifierType; private DataModelDynamicViewModel _dynamicSelectionViewModel; + private DataBindingModifierType _selectedModifierType; private DataModelStaticViewModel _staticInputViewModel; - public DataBindingModifierViewModel(DataBindingModifier modifier, + public DataBindingModifierViewModel(DataBindingModifier modifier, IDataBindingService dataBindingService, ISettingsService settingsService, IDataModelUIService dataModelUIService, @@ -42,7 +40,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings public DelegateCommand SelectModifierTypeCommand { get; } public PluginSetting ShowDataModelValues { get; } - public DataBindingModifier Modifier { get; } + public DataBindingModifier Modifier { get; } public BindableCollection ModifierTypes { get; } public DataBindingModifierType SelectedModifierType @@ -62,7 +60,22 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings get => _staticInputViewModel; private set => SetAndNotify(ref _staticInputViewModel, value); } - + + public void Delete() + { + Modifier.DataBinding.RemoveModifier(Modifier); + } + + public void SwapType() + { + if (Modifier.ParameterType == ProfileRightSideType.Dynamic) + Modifier.UpdateParameter(Modifier.DataBinding.GetSourceType().GetDefault()); + else + Modifier.UpdateParameter(null, null); + + Update(); + } + private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) { Modifier.UpdateParameter(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.PropertyPath); @@ -89,7 +102,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings StaticInputViewModel = null; DynamicSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); DynamicSelectionViewModel.PropertySelected += ParameterSelectionViewModelOnPropertySelected; - DynamicSelectionViewModel.FilterTypes = new[] { sourceType }; + DynamicSelectionViewModel.FilterTypes = new[] {sourceType}; } else { @@ -120,20 +133,5 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings Update(); } - - public void Delete() - { - Modifier.DataBinding.RemoveModifier(Modifier); - } - - public void SwapType() - { - if (Modifier.ParameterType == ProfileRightSideType.Dynamic) - Modifier.UpdateParameter(Modifier.DataBinding.GetSourceType().GetDefault()); - else - Modifier.UpdateParameter(null, null); - - Update(); - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs index 6397b9437..dc89d11a1 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingViewModel.cs @@ -10,23 +10,23 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { - public class DataBindingViewModel : PropertyChangedBase, IDisposable + public class DataBindingViewModel : Screen, IDataBindingViewModel { private readonly IDataBindingsVmFactory _dataBindingsVmFactory; private readonly IDataModelUIService _dataModelUIService; private readonly IProfileEditorService _profileEditorService; - private DataBinding _dataBinding; + private DataBinding _dataBinding; private int _easingTime; private bool _isDataBindingEnabled; private bool _isEasingTimeEnabled; private DataBindingMode _selectedDataBindingMode; private TimelineEasingViewModel _selectedEasingViewModel; private DataModelDynamicViewModel _targetSelectionViewModel; - private object _testInputValue; - private object _testResultValue; + private TProperty _testInputValue; + private TProperty _testResultValue; private bool _updating; - public DataBindingViewModel(DataBindingRegistration registration, + public DataBindingViewModel(DataBindingRegistration registration, IProfileEditorService profileEditorService, IDataModelUIService dataModelUIService, IDataBindingsVmFactory dataBindingsVmFactory) @@ -40,7 +40,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings DataBindingModes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingMode))); EasingViewModels = new BindableCollection(); - ModifierViewModels = new BindableCollection(); + ModifierViewModels = new BindableCollection>(); DataBinding = Registration.DataBinding; if (DataBinding != null) @@ -52,12 +52,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings Execute.PostToUIThread(Initialize); } - public DataBindingRegistration Registration { get; } - public string DisplayName { get; } + public DataBindingRegistration Registration { get; } public BindableCollection DataBindingModes { get; } public BindableCollection EasingViewModels { get; } - public BindableCollection ModifierViewModels { get; } + public BindableCollection> ModifierViewModels { get; } public DataBindingMode SelectedDataBindingMode { @@ -114,7 +113,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings } } - public DataBinding DataBinding + public DataBinding DataBinding { get => _dataBinding; set @@ -124,13 +123,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings } } - public object TestInputValue + public TProperty TestInputValue { get => _testInputValue; set => SetAndNotify(ref _testInputValue, value); } - public object TestResultValue + public TProperty TestResultValue { get => _testResultValue; set => SetAndNotify(ref _testResultValue, value); @@ -167,7 +166,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings if (DataBinding == null) return; - var modifier = new DataBindingModifier(ProfileRightSideType.Dynamic); + var modifier = new DataBindingModifier(ProfileRightSideType.Dynamic); DataBinding.AddModifier(modifier); _profileEditorService.UpdateSelectedProfileElement(); @@ -175,7 +174,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings private void Initialize() { - EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast().Select(v => new TimelineEasingViewModel(null, v))); + EasingViewModels.AddRange(Enum.GetValues(typeof(Easings.Functions)).Cast().Select(v => new TimelineEasingViewModel(v, false))); TargetSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_profileEditorService.GetCurrentModule()); TargetSelectionViewModel.PropertySelected += TargetSelectionViewModelOnPropertySelected; _profileEditorService.ProfilePreviewUpdated += ProfileEditorServiceOnProfilePreviewUpdated; @@ -229,9 +228,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings var currentValue = TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue(); if (currentValue == null && Registration.Property.PropertyType.IsValueType) currentValue = Activator.CreateInstance(Registration.Property.PropertyType); - - TestInputValue = Convert.ChangeType(currentValue, Registration.Property.PropertyType); - TestResultValue = DataBinding?.GetValue(TestInputValue); + + TestInputValue = currentValue is TProperty testInputValue ? testInputValue : default; + if (DataBinding != null) + TestResultValue = DataBinding.GetValue(TestInputValue); + else + TestInputValue = default; } private void UpdateModifierViewModels() @@ -268,4 +270,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings DataBinding.ModifiersUpdated -= DataBindingOnModifiersUpdated; } } + + public interface IDataBindingViewModel : IScreen, IDisposable + { + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsView.xaml deleted file mode 100644 index 371cdb7a6..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsView.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsViewModel.cs deleted file mode 100644 index 18355e690..000000000 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsTabsViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Stylet; - -namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings -{ - public class DataBindingsTabsViewModel : PropertyChangedBase - { - public DataBindingsTabsViewModel() - { - Tabs = new BindableCollection(); - } - - public BindableCollection Tabs { get; set; } - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml index 625d041b5..85def1ef5 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsView.xaml @@ -3,14 +3,14 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings" xmlns:s="https://github.com/canton7/Stylet" mc:Ignorable="d" - d:DesignHeight="450" d:DesignWidth="800" - d:DataContext="{d:DesignInstance local:DataBindingsViewModel}"> - - - - - + 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 585abf21f..bebf2d871 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/DataBindings/DataBindingsViewModel.cs @@ -6,58 +6,27 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings { - public class DataBindingsViewModel : PropertyChangedBase, IDisposable + public class DataBindingsViewModel : Conductor.Collection.AllActive { private readonly IDataBindingsVmFactory _dataBindingsVmFactory; - private DataBindingsTabsViewModel _dataBindingsTabsViewModel; - private DataBindingViewModel _dataBindingViewModel; - - public DataBindingsViewModel(BaseLayerProperty layerProperty, IDataBindingsVmFactory dataBindingsVmFactory) + + public DataBindingsViewModel(LayerProperty layerProperty, IDataBindingsVmFactory dataBindingsVmFactory) { _dataBindingsVmFactory = dataBindingsVmFactory; LayerProperty = layerProperty; Initialise(); } - public BaseLayerProperty LayerProperty { get; } - - public DataBindingViewModel DataBindingViewModel - { - get => _dataBindingViewModel; - set => SetAndNotify(ref _dataBindingViewModel, value); - } - - public DataBindingsTabsViewModel DataBindingsTabsViewModel - { - get => _dataBindingsTabsViewModel; - set => SetAndNotify(ref _dataBindingsTabsViewModel, value); - } + public LayerProperty LayerProperty { get; } private void Initialise() { - DataBindingViewModel?.Dispose(); - DataBindingViewModel = null; - DataBindingsTabsViewModel = null; - - var registrations = LayerProperty.DataBindingRegistrations; - if (registrations == null || registrations.Count == 0) - return; + var registrations = LayerProperty.GetAllDataBindingRegistrations(); // Create a data binding VM for each data bindable property. These VMs will be responsible for retrieving // and creating the actual data bindings - if (registrations.Count == 1) - DataBindingViewModel = _dataBindingsVmFactory.DataBindingViewModel(registrations.First()); - else - { - DataBindingsTabsViewModel = new DataBindingsTabsViewModel(); - foreach (var registration in registrations) - DataBindingsTabsViewModel.Tabs.Add(_dataBindingsVmFactory.DataBindingViewModel(registration)); - } - } - - public void Dispose() - { - DataBindingViewModel?.Dispose(); + foreach (var registration in registrations) + ActivateItem(_dataBindingsVmFactory.DataBindingViewModel(registration)); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index e3a6c0974..0e5311a24 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -289,28 +289,26 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties SelectedLayer.LayerBrushUpdated += SelectedLayerOnLayerBrushUpdated; // Add the built-in root groups of the layer - var generalAttribute = Attribute.GetCustomAttribute( - SelectedLayer.GetType().GetProperty(nameof(SelectedLayer.General)), - typeof(PropertyGroupDescriptionAttribute) - ); - var transformAttribute = Attribute.GetCustomAttribute( - SelectedLayer.GetType().GetProperty(nameof(SelectedLayer.Transform)), - typeof(PropertyGroupDescriptionAttribute) - ); - LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.General, (PropertyGroupDescriptionAttribute) generalAttribute)); - LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.Transform, (PropertyGroupDescriptionAttribute) transformAttribute)); + LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.General)); + LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.Transform)); } TreeViewModel = _layerPropertyVmFactory.TreeViewModel(this, LayerPropertyGroups); - TimelineViewModel?.Dispose(); + DeactivateItem(TimelineViewModel); + DeactivateItem(StartTimelineSegmentViewModel); + DeactivateItem(MainTimelineSegmentViewModel); + DeactivateItem(EndTimelineSegmentViewModel); + TimelineViewModel = _layerPropertyVmFactory.TimelineViewModel(this, LayerPropertyGroups); + ActivateItem(TimelineViewModel); + StartTimelineSegmentViewModel?.Dispose(); - StartTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.Start); + StartTimelineSegmentViewModel = _layerPropertyVmFactory.TimelineSegmentViewModel(SegmentViewModelType.Start, LayerPropertyGroups); MainTimelineSegmentViewModel?.Dispose(); - MainTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.Main); + MainTimelineSegmentViewModel = _layerPropertyVmFactory.TimelineSegmentViewModel(SegmentViewModelType.Main, LayerPropertyGroups); EndTimelineSegmentViewModel?.Dispose(); - EndTimelineSegmentViewModel = new TimelineSegmentViewModel(ProfileEditorService, SegmentViewModelType.End); + EndTimelineSegmentViewModel = _layerPropertyVmFactory.TimelineSegmentViewModel(SegmentViewModelType.End, LayerPropertyGroups); ApplyLayerBrush(); ApplyEffects(); @@ -346,19 +344,19 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties if (SelectedLayer.LayerBrush != null) { - // Add the rout group of the brush + // TODO: wat? + // Add the root group of the brush // The root group of the brush has no attribute so let's pull one out of our sleeve var brushDescription = new PropertyGroupDescriptionAttribute { Name = SelectedLayer.LayerBrush.Descriptor.DisplayName, Description = SelectedLayer.LayerBrush.Descriptor.Description }; - _brushPropertyGroup = _layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.LayerBrush.BaseProperties, brushDescription); + _brushPropertyGroup = _layerPropertyVmFactory.LayerPropertyGroupViewModel(SelectedLayer.LayerBrush.BaseProperties); LayerPropertyGroups.Add(_brushPropertyGroup); } SortProperties(); - TimelineViewModel.Update(); } private void ApplyEffects() @@ -382,18 +380,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties if (LayerPropertyGroups.Any(l => l.LayerPropertyGroup.LayerEffect == layerEffect)) continue; - // Add the rout group of the brush + // TODO: wat? + // Add the root group of the brush // The root group of the brush has no attribute so let's pull one out of our sleeve var brushDescription = new PropertyGroupDescriptionAttribute { Name = layerEffect.Descriptor.DisplayName, Description = layerEffect.Descriptor.Description }; - LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerEffect.BaseProperties, brushDescription)); + LayerPropertyGroups.Add(_layerPropertyVmFactory.LayerPropertyGroupViewModel(layerEffect.BaseProperties)); } SortProperties(); - TimelineViewModel.Update(); } private void SortProperties() @@ -422,7 +420,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties LayerPropertyGroups.Move(LayerPropertyGroups.IndexOf(layerPropertyGroupViewModel), index + nonEffectProperties.Count); } } - + #endregion #region Drag and drop @@ -437,8 +435,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties var source = dropInfo.Data as LayerPropertyGroupViewModel; var target = dropInfo.TargetItem as LayerPropertyGroupViewModel; - if (source == target || - target?.TreeGroupViewModel.GroupType != LayerEffectRoot || + if (source == target || + target?.TreeGroupViewModel.GroupType != LayerEffectRoot || source?.TreeGroupViewModel.GroupType != LayerEffectRoot) return; @@ -557,13 +555,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties private TimeSpan CalculateEndTime() { - var keyframeTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframePositions(false)).ToList(); + var keyframeViewModels = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframeViewModels(false)).ToList(); // If there are no keyframes, don't stop at all - if (!keyframeTimes.Any()) + if (!keyframeViewModels.Any()) return TimeSpan.MaxValue; // If there are keyframes, stop after the last keyframe + 10 sec - return keyframeTimes.Max().Add(TimeSpan.FromSeconds(10)); + return keyframeViewModels.Max(k => k.Position).Add(TimeSpan.FromSeconds(10)); } private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e) @@ -620,7 +618,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties // If holding down shift, snap to the closest segment or keyframe if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { - var snapTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframePositions(true)).ToList(); + var snapTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframeViewModels(true)).Select(k => k.Position).ToList(); var snappedTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), true, false, snapTimes); ProfileEditorService.CurrentTime = snappedTime; return; diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs index 09f4e5132..67ced6517 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs @@ -82,21 +82,54 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties NotifyOfPropertyChange(nameof(IsExpanded)); } - public List GetAllKeyframePositions(bool expandedOnly) + public List GetAllKeyframeViewModels(bool expandedOnly) { - var result = new List(); + var result = new List(); if (expandedOnly == IsExpanded) return result; foreach (var child in Children) { if (child is LayerPropertyViewModel layerPropertyViewModel) - result.AddRange(layerPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframePositions()); + result.AddRange(layerPropertyViewModel.TimelinePropertyViewModel.GetAllKeyframeViewModels()); else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel) - result.AddRange(layerPropertyGroupViewModel.GetAllKeyframePositions(expandedOnly)); + result.AddRange(layerPropertyGroupViewModel.GetAllKeyframeViewModels(expandedOnly)); } return result; } + + /// + /// Removes the keyframes between the and position from this property group + /// + /// The position at which to start removing keyframes, if null this will start at the first keyframe + /// The position at which to start removing keyframes, if null this will end at the last keyframe + public virtual void WipeKeyframes(TimeSpan? start, TimeSpan? end) + { + foreach (var child in Children) + { + if (child is LayerPropertyViewModel layerPropertyViewModel) + layerPropertyViewModel.TimelinePropertyViewModel.WipeKeyframes(start, end); + else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel) + layerPropertyGroupViewModel.WipeKeyframes(start, end); + } + } + + /// + /// Shifts the keyframes between the and position by the provided + /// + /// The position at which to start shifting keyframes, if null this will start at the first keyframe + /// The position at which to start shifting keyframes, if null this will end at the last keyframe + /// The amount to shift the keyframes for + public void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount) + { + foreach (var child in Children) + { + if (child is LayerPropertyViewModel layerPropertyViewModel) + layerPropertyViewModel.TimelinePropertyViewModel.ShiftKeyframes(start, end, amount); + else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel) + layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount); + } + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs index 9e40b1bb8..cafd20ac0 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineGroupViewModel.cs @@ -41,8 +41,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline { KeyframePositions.Clear(); KeyframePositions.AddRange(LayerPropertyGroupViewModel - .GetAllKeyframePositions(false) - .Select(p => p.TotalSeconds * _profileEditorService.PixelsPerSecond)); + .GetAllKeyframeViewModels(false) + .Select(p => p.Position.TotalSeconds * _profileEditorService.PixelsPerSecond)); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs index c586ce613..03c1a0635 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineKeyframeViewModel.cs @@ -7,7 +7,7 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline { - public class TimelineKeyframeViewModel : Screen, IDisposable + public class TimelineKeyframeViewModel : Screen, ITimelineKeyframeViewModel { private readonly IProfileEditorService _profileEditorService; @@ -31,12 +31,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline set => SetAndNotify(ref _easingViewModels, value); } - public bool IsSelected - { - get => _isSelected; - set => SetAndNotify(ref _isSelected, value); - } - public double X { get => _x; @@ -49,6 +43,14 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline set => SetAndNotify(ref _timestamp, value); } + public bool IsSelected + { + get => _isSelected; + set => SetAndNotify(ref _isSelected, value); + } + + public TimeSpan Position => LayerPropertyKeyframe.Position; + public void Dispose() { LayerPropertyKeyframe.PropertyChanged -= LayerPropertyKeyframeOnPropertyChanged; @@ -110,9 +112,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline _offset = null; } - public void SaveOffsetToKeyframe(TimelineKeyframeViewModel keyframeViewModel) + public void SaveOffsetToKeyframe(ITimelineKeyframeViewModel source) { - if (keyframeViewModel == this) + if (source == this) { _offset = null; return; @@ -121,15 +123,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline if (_offset != null) return; - _offset = LayerPropertyKeyframe.Position - keyframeViewModel.LayerPropertyKeyframe.Position; + _offset = LayerPropertyKeyframe.Position - source.Position; } - public void ApplyOffsetToKeyframe(TimelineKeyframeViewModel keyframeViewModel) + public void ApplyOffsetToKeyframe(ITimelineKeyframeViewModel source) { - if (keyframeViewModel == this || _offset == null) + if (source == this || _offset == null) return; - UpdatePosition(keyframeViewModel.LayerPropertyKeyframe.Position + _offset.Value); + UpdatePosition(source.Position + _offset.Value); } public void UpdatePosition(TimeSpan position) @@ -148,6 +150,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline #region Context menu actions + public void ContextMenuOpening() + { + CreateEasingViewModels(); + } + + public void ContextMenuClosing() + { + foreach (var timelineEasingViewModel in EasingViewModels) + timelineEasingViewModel.EasingModeSelected -= TimelineEasingViewModelOnEasingModeSelected; + EasingViewModels.Clear(); + } + public void Copy() { var newKeyframe = new LayerPropertyKeyframe( @@ -180,4 +194,26 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline #endregion } + + public interface ITimelineKeyframeViewModel : IScreen, IDisposable + { + bool IsSelected { get; set; } + TimeSpan Position { get; } + + #region Movement + + void SaveOffsetToKeyframe(ITimelineKeyframeViewModel source); + void ApplyOffsetToKeyframe(ITimelineKeyframeViewModel source); + void UpdatePosition(TimeSpan position); + void ReleaseMovement(); + + #endregion + + #region Context menu actions + + void Copy(); + void Delete(); + + #endregion + } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs index 320bf9099..c4426894a 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelinePropertyViewModel.cs @@ -21,9 +21,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline LayerPropertyViewModel = layerPropertyViewModel; } - public List GetAllKeyframePositions() + public List GetAllKeyframeViewModels() { - return LayerProperty.Keyframes.Select(k => k.Position).ToList(); + return Items.Cast().ToList(); } private void UpdateKeyframes() @@ -49,6 +49,30 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline timelineKeyframeViewModel.Update(); } + public void WipeKeyframes(TimeSpan? start, TimeSpan? end) + { + start ??= TimeSpan.Zero; + end ??= TimeSpan.MaxValue; + + var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position <= end).ToList(); + foreach (var keyframe in toShift) + LayerProperty.RemoveKeyframe(keyframe); + + UpdateKeyframes(); + } + + public void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount) + { + start ??= TimeSpan.Zero; + end ??= TimeSpan.MaxValue; + + var toShift = LayerProperty.Keyframes.Where(k => k.Position >= start && k.Position <= end).ToList(); + foreach (var keyframe in toShift) + keyframe.Position += amount; + + UpdateKeyframes(); + } + public void Dispose() { } @@ -56,6 +80,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline public interface ITimelinePropertyViewModel : IScreen, IDisposable { - List GetAllKeyframePositions(); + List GetAllKeyframeViewModels(); + void WipeKeyframes(TimeSpan? start, TimeSpan? end); + void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs index 76a9c0c19..5404a6322 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineSegmentViewModel.cs @@ -18,10 +18,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline private bool _showRepeatButton; private bool _showSegmentName; - public TimelineSegmentViewModel(IProfileEditorService profileEditorService, SegmentViewModelType segment) + public TimelineSegmentViewModel(SegmentViewModelType segment, BindableCollection layerPropertyGroups, + IProfileEditorService profileEditorService) { ProfileEditorService = profileEditorService; Segment = segment; + LayerPropertyGroups = layerPropertyGroups; SelectedProfileElement = ProfileEditorService.SelectedProfileElement; switch (Segment) @@ -47,6 +49,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline public RenderProfileElement SelectedProfileElement { get; } public SegmentViewModelType Segment { get; } + public BindableCollection LayerPropertyGroups { get; } public IProfileEditorService ProfileEditorService { get; } public string ToolTip { get; } @@ -130,7 +133,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline public void DisableSegment() { - var keyframes = SelectedProfileElement.GetAllKeyframes(); var startSegmentEnd = SelectedProfileElement.StartSegmentLength; var mainSegmentEnd = SelectedProfileElement.StartSegmentLength + SelectedProfileElement.MainSegmentLength; @@ -139,22 +141,19 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline if (Segment == SegmentViewModelType.Start) { // Remove keyframes that fall in this segment - foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position <= startSegmentEnd)) - baseLayerPropertyKeyframe.Remove(); + WipeKeyframes(null, startSegmentEnd); SelectedProfileElement.StartSegmentLength = TimeSpan.Zero; } else if (Segment == SegmentViewModelType.Main) { // Remove keyframes that fall in this segment - foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position > startSegmentEnd && k.Position <= mainSegmentEnd)) - baseLayerPropertyKeyframe.Remove(); + WipeKeyframes(startSegmentEnd, startSegmentEnd); SelectedProfileElement.MainSegmentLength = TimeSpan.Zero; } else if (Segment == SegmentViewModelType.End) { // Remove keyframes that fall in this segment - foreach (var baseLayerPropertyKeyframe in keyframes.Where(k => k.Position > mainSegmentEnd)) - baseLayerPropertyKeyframe.Remove(); + WipeKeyframes(mainSegmentEnd, null); SelectedProfileElement.EndSegmentLength = TimeSpan.Zero; } @@ -188,8 +187,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline else if (Segment == SegmentViewModelType.End) segmentEnd = SelectedProfileElement.TimelineLength; - foreach (var baseLayerPropertyKeyframe in SelectedProfileElement.GetAllKeyframes().Where(k => k.Position > segmentEnd)) - baseLayerPropertyKeyframe.Position += amount; + ShiftKeyframes(segmentEnd, null, amount); } public void SegmentMouseDown(object sender, MouseButtonEventArgs e) @@ -227,7 +225,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline // If holding down shift, snap to the closest element on the timeline if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) - newTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), false, true, true); + { + var keyframeTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframeViewModels(true)).Select(k => k.Position).ToList(); + newTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), false, true, keyframeTimes); + } // If holding down control, round to the closest 50ms else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 50.0) * 50.0); @@ -272,7 +273,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline NotifyOfPropertyChange(nameof(RepeatSegment)); } - private void ProfileEditorServiceOnPixelsPerSecondChanged(object? sender, EventArgs e) { NotifyOfPropertyChange(nameof(SegmentWidth)); @@ -291,6 +291,18 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline ShowRepeatButton = SegmentWidth > 45 && IsMainSegment; ShowDisableButton = SegmentWidth > 25; } + + private void WipeKeyframes(TimeSpan? start, TimeSpan? end) + { + foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) + layerPropertyGroupViewModel.WipeKeyframes(start, end); + } + + private void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount) + { + foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) + layerPropertyGroupViewModel.ShiftKeyframes(start, end, amount); + } } public enum SegmentViewModelType diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs index 263167481..711949901 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/TimelineViewModel.cs @@ -14,7 +14,7 @@ using Stylet; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline { - public class TimelineViewModel : PropertyChangedBase, IViewAware, IDisposable + public class TimelineViewModel : Screen, IDisposable { private readonly LayerPropertiesViewModel _layerPropertiesViewModel; private readonly IProfileEditorService _profileEditorService; @@ -105,7 +105,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline // if (e.LeftButton == MouseButtonState.Released) // return; - var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel; + var viewModel = (sender as Ellipse)?.DataContext as ITimelineKeyframeViewModel; if (viewModel == null) return; @@ -130,7 +130,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline public void KeyframeMouseMove(object sender, MouseEventArgs e) { - var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel; + var viewModel = (sender as Ellipse)?.DataContext as ITimelineKeyframeViewModel; if (viewModel == null) return; @@ -142,24 +142,12 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline #region Context menu actions - public void ContextMenuOpening(object sender, ContextMenuEventArgs e) - { - var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel; - viewModel?.CreateEasingViewModels(); - } - - public void ContextMenuClosing(object sender, ContextMenuEventArgs e) - { - var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel; - viewModel?.EasingViewModels.Clear(); - } - - public void Copy(TimelineKeyframeViewModel viewModel) + public void Copy(ITimelineKeyframeViewModel viewModel) { viewModel.Copy(); } - public void Delete(TimelineKeyframeViewModel viewModel) + public void Delete(ITimelineKeyframeViewModel viewModel) { viewModel.Delete(); } @@ -198,7 +186,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline #region Keyframe movement - public void MoveSelectedKeyframes(TimeSpan cursorTime, TimelineKeyframeViewModel sourceKeyframeViewModel) + public void MoveSelectedKeyframes(TimeSpan cursorTime, ITimelineKeyframeViewModel sourceKeyframeViewModel) { // Ensure the selection rectangle doesn't show, the view isn't aware of different types of dragging SelectionRectangle.Rect = new Rect(); @@ -214,8 +202,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline TimeSpan.FromMilliseconds(1000f / _profileEditorService.PixelsPerSecond * 5), true, false, - true, - sourceKeyframeViewModel.BaseLayerPropertyKeyframe + keyframeViewModels.Where(k => k != sourceKeyframeViewModel).Select(k => k.Position).ToList() ); } @@ -267,7 +254,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline SelectionRectangle.Rect = selectedRect; var keyframeViewModels = GetAllKeyframeViewModels(); - var selectedKeyframes = HitTestUtilities.GetHitViewModels((Visual) sender, SelectionRectangle); + var selectedKeyframes = HitTestUtilities.GetHitViewModels((Visual) sender, SelectionRectangle); foreach (var keyframeViewModel in keyframeViewModels) keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel); @@ -287,7 +274,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline } } - public void SelectKeyframe(TimelineKeyframeViewModel clicked, bool selectBetween, bool toggle) + public void SelectKeyframe(ITimelineKeyframeViewModel clicked, bool selectBetween, bool toggle) { var keyframeViewModels = GetAllKeyframeViewModels(); if (selectBetween) @@ -329,33 +316,15 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline } } - private List GetAllKeyframeViewModels() + private List GetAllKeyframeViewModels() { - var viewModels = new List(); + var viewModels = new List(); foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) - viewModels.AddRange(layerPropertyGroupViewModel.GetAllChildren()); - - var keyframes = viewModels.Where(vm => vm is LayerPropertyViewModel) - .SelectMany(vm => ((LayerPropertyViewModel) vm).TimelinePropertyBaseViewModel.TimelineKeyframeViewModels) - .ToList(); - - return keyframes; + viewModels.AddRange(layerPropertyGroupViewModel.GetAllKeyframeViewModels(false)); + + return viewModels; } #endregion - - #region IViewAware - - public void AttachView(UIElement view) - { - if (View != null) - throw new InvalidOperationException(string.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, GetType().Name)); - - View = view; - } - - public UIElement View { get; set; } - - #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs index c6e84ce99..46d952de5 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Windows; using System.Windows.Input; using Artemis.Core; -using Artemis.Core.Services; using Artemis.UI.Properties; using Artemis.UI.Shared.Services; @@ -11,13 +10,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools { public class SelectionToolViewModel : VisualizationToolViewModel { - private readonly IRenderElementService _renderElementService; private Rect _dragRectangle; - public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, IRenderElementService renderElementService) - : base(profileViewModel, profileEditorService) + public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService) { - _renderElementService = renderElementService; using (var stream = new MemoryStream(Resources.aero_crosshair)) { Cursor = new Cursor(stream); @@ -57,7 +53,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools // If no layer selected, apply it to a new layer in the selected folder else if (ProfileEditorService.SelectedProfileElement is Folder folder) { - var newLayer = _renderElementService.CreateLayer(folder.Profile, folder, "New layer"); + var newLayer = new Layer(folder, "New layer"); newLayer.AddLeds(selectedLeds); ProfileEditorService.ChangeSelectedProfileElement(newLayer); ProfileEditorService.UpdateSelectedProfileElement(); @@ -66,7 +62,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Visualization.Tools else { var rootFolder = ProfileEditorService.SelectedProfile.GetRootFolder(); - var newLayer = _renderElementService.CreateLayer(rootFolder.Profile, rootFolder, "New layer"); + var newLayer = new Layer(rootFolder, "New layer"); newLayer.AddLeds(selectedLeds); ProfileEditorService.ChangeSelectedProfileElement(newLayer); ProfileEditorService.UpdateSelectedProfileElement();