From 0c245ba83d6867907007aec2768d01ed0bc6817e Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 14 Jan 2020 19:03:35 +0100 Subject: [PATCH] Added undo/redo to profile editor Added shape anchor point display and movement --- src/Artemis.Core/Models/Profile/Layer.cs | 2 +- .../Profile/LayerProperties/BaseKeyframe.cs | 2 +- .../LayerProperties/BaseLayerProperty.cs | 41 +++++- .../Profile/LayerProperties/LayerProperty.cs | 27 ++-- .../Models/Profile/LayerShapes/LayerShape.cs | 37 +++++ src/Artemis.Core/Models/Profile/Profile.cs | 32 ++++- .../Storage/Interfaces/IProfileService.cs | 14 ++ .../Services/Storage/ProfileService.cs | 34 +++++ .../Ninject/Factories/IViewModelFactory.cs | 2 +- .../LayerProperties/LayerPropertyViewModel.cs | 2 +- .../PropertyInput/FloatPropertyInputView.xaml | 3 +- .../PropertyInput/IntPropertyInputView.xaml | 3 +- .../PropertyInput/PropertyInputViewModel.cs | 2 +- .../SKPointPropertyInputView.xaml | 6 +- .../SKSizePropertyInputView.xaml | 6 +- .../PropertyTrackKeyframeViewModel.cs | 27 +++- .../Timeline/PropertyTrackView.xaml | 12 +- .../Timeline/PropertyTrackViewModel.cs | 2 +- .../ProfileEditor/ProfileEditorView.xaml | 5 + .../ProfileEditor/ProfileEditorViewModel.cs | 10 ++ .../Visualization/ProfileLayerView.xaml | 2 +- .../Visualization/Tools/EditToolView.xaml | 18 ++- .../Visualization/Tools/EditToolViewModel.cs | 127 +++++++++++++++++- .../Interfaces/IProfileEditorService.cs | 8 +- .../Services/ProfileEditorService.cs | 30 ++++- 25 files changed, 386 insertions(+), 68 deletions(-) diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 3ad462f2f..d0878d9a9 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -290,7 +290,7 @@ namespace Artemis.Core.Models.Profile Path = path; // This is called here so that the shape's render properties are up to date when other code // responds to OnRenderPropertiesUpdated - LayerShape?.CalculateRenderProperties(PositionProperty.GetCurrentValue(), SizeProperty.GetCurrentValue()); + LayerShape?.CalculateRenderProperties(PositionProperty.CurrentValue, SizeProperty.CurrentValue); OnRenderPropertiesUpdated(); } diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs index f4b6d4d19..2f68a2a71 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs @@ -27,7 +27,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties } protected BaseLayerProperty BaseProperty { get; } - protected internal object BaseValue { get; set; } + public object BaseValue { get; internal set; } public Easings.Functions EasingFunction { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs index 82fb7d75f..ead38477f 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs @@ -101,10 +101,10 @@ namespace Artemis.Core.Models.Profile.LayerProperties protected List BaseKeyframes { get; set; } - protected internal object BaseValue + public object BaseValue { get => _baseValue; - set + internal set { if (value != null && value.GetType() != Type) throw new ArtemisCoreException($"Cannot set value of type {value.GetType()} on property {this}, expected type is {Type}."); @@ -164,13 +164,13 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// Creates a new keyframe for this base property without knowing the type /// /// - public BaseKeyframe CreateNewKeyframe(TimeSpan position) + public BaseKeyframe CreateNewKeyframe(TimeSpan position, object value) { // Create a strongly typed keyframe or else it cannot be cast later on var keyframeType = typeof(Keyframe<>); var keyframe = (BaseKeyframe) Activator.CreateInstance(keyframeType.MakeGenericType(Type), Layer, this); keyframe.Position = position; - keyframe.BaseValue = BaseValue; + keyframe.BaseValue = value; BaseKeyframes.Add(keyframe); SortKeyframes(); @@ -186,6 +186,17 @@ namespace Artemis.Core.Models.Profile.LayerProperties BaseKeyframes.Clear(); } + /// + /// Gets the current value using the regular value or if present, keyframes + /// + public object GetCurrentValue() + { + if (KeyframeEngine == null || !UntypedKeyframes.Any()) + return BaseValue; + + return KeyframeEngine.GetCurrentValue(); + } + /// /// Gets the current value using the regular value or keyframes. /// @@ -207,12 +218,32 @@ namespace Artemis.Core.Models.Profile.LayerProperties var currentKeyframe = UntypedKeyframes.FirstOrDefault(k => k.Position == time.Value); // Create a new keyframe if none found if (currentKeyframe == null) - currentKeyframe = CreateNewKeyframe(time.Value); + currentKeyframe = CreateNewKeyframe(time.Value, value); currentKeyframe.BaseValue = value; } } + /// + /// Adds a keyframe to the property. + /// + /// The keyframe to remove + public void AddKeyframe(BaseKeyframe keyframe) + { + BaseKeyframes.Add(keyframe); + SortKeyframes(); + } + + /// + /// Removes a keyframe from the property. + /// + /// The keyframe to remove + public void RemoveKeyframe(BaseKeyframe keyframe) + { + BaseKeyframes.Remove(keyframe); + SortKeyframes(); + } + internal void SortKeyframes() { BaseKeyframes = BaseKeyframes.OrderBy(k => k.Position).ToList(); diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 34900c9bc..54c393ff0 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -22,7 +22,14 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// /// Gets the value of the property with keyframes applied /// - public T CurrentValue => KeyframeEngine != null ? (T) KeyframeEngine.GetCurrentValue() : Value; + public T CurrentValue + { + get + { + var currentValue = base.GetCurrentValue(); + return currentValue == null ? default : (T)currentValue; + } + } /// /// Gets a list of keyframes defining different values of the property in time, this list contains the strongly typed @@ -36,8 +43,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// The keyframe to remove public void AddKeyframe(Keyframe keyframe) { - BaseKeyframes.Add(keyframe); - SortKeyframes(); + base.AddKeyframe(keyframe); } /// @@ -46,20 +52,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// The keyframe to remove public void RemoveKeyframe(Keyframe keyframe) { - BaseKeyframes.Remove(keyframe); - SortKeyframes(); - } - - /// - /// Gets the current value using the regular value or if present, keyframes - /// - /// - public T GetCurrentValue() - { - if (KeyframeEngine == null || !Keyframes.Any()) - return Value; - - return (T) KeyframeEngine.GetCurrentValue(); + base.RemoveKeyframe(keyframe); } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs index fa5e217d7..229287c62 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs @@ -81,5 +81,42 @@ namespace Artemis.Core.Models.Profile.LayerShapes (float) (height * Layer.SizeProperty.CurrentValue.Height) ); } + + public void SetFromUnscaledAnchor(SKPoint anchor, TimeSpan? time) + { + if (!Layer.Leds.Any()) + { + Layer.PositionProperty.SetCurrentValue(SKPoint.Empty, time); + Layer.SizeProperty.SetCurrentValue(SKSize.Empty, time); + return; + } + + var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); + var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); + var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; + var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; + + Layer.AnchorPointProperty.SetCurrentValue(new SKPoint( + (float) (100f / width * (anchor.X - x - Layer.PositionProperty.CurrentValue.X)) / 100f, + (float) (100f / height * (anchor.Y - y - Layer.PositionProperty.CurrentValue.Y)) / 100f + ), time); + CalculateRenderProperties(Layer.PositionProperty.CurrentValue, Layer.SizeProperty.CurrentValue); + } + + public SKPoint GetUnscaledAnchor() + { + if (!Layer.Leds.Any()) + return SKPoint.Empty; + + var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); + var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); + var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; + var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; + + return new SKPoint( + (float) (x + width * (Layer.AnchorPointProperty.CurrentValue.X + Layer.PositionProperty.CurrentValue.X)), + (float) (y + height * (Layer.AnchorPointProperty.CurrentValue.Y + Layer.PositionProperty.CurrentValue.Y)) + ); + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index 87e67a462..3286637e7 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Artemis.Core.Exceptions; using Artemis.Core.Models.Surface; @@ -17,6 +18,8 @@ namespace Artemis.Core.Models.Profile PluginInfo = pluginInfo; Name = name; + UndoStack = new Stack(); + RedoStack = new Stack(); AddChild(new Folder(this, null, "Root folder")); ApplyToEntity(); @@ -28,14 +31,10 @@ namespace Artemis.Core.Models.Profile EntityId = profileEntity.Id; PluginInfo = pluginInfo; - Name = profileEntity.Name; + UndoStack = new Stack(); + RedoStack = new Stack(); - // Populate the profile starting at the root, the rest is populated recursively - var rootFolder = profileEntity.Folders.FirstOrDefault(f => f.ParentId == new Guid()); - if (rootFolder == null) - AddChild(new Folder(this, null, "Root folder")); - else - AddChild(new Folder(this, null, rootFolder)); + ApplyToProfile(); } public PluginInfo PluginInfo { get; } @@ -43,6 +42,9 @@ namespace Artemis.Core.Models.Profile internal ProfileEntity ProfileEntity { get; set; } + internal Stack UndoStack { get; set; } + internal Stack RedoStack { get; set; } + public override void Update(double deltaTime) { lock (this) @@ -89,6 +91,22 @@ namespace Artemis.Core.Models.Profile ProfileEntity.Layers.AddRange(GetAllLayers().Select(f => f.LayerEntity)); } + public void ApplyToProfile() + { + Name = ProfileEntity.Name; + + lock (_children) + { + _children.Clear(); + // Populate the profile starting at the root, the rest is populated recursively + var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == new Guid()); + if (rootFolder == null) + AddChild(new Folder(this, null, "Root folder")); + else + AddChild(new Folder(this, null, rootFolder)); + } + } + internal void Activate(ArtemisSurface surface) { lock (this) diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs index 269db16ea..d9139cd9e 100644 --- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs +++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs @@ -19,5 +19,19 @@ namespace Artemis.Core.Services.Storage.Interfaces /// The module to activate the profile for /// The profile to activate void ActivateProfile(ProfileModule module, Profile profile); + + /// + /// Attempts to restore the profile to the state it had before the last call. + /// + /// + /// + void UndoUpdateProfile(Profile selectedProfile, ProfileModule module); + + /// + /// Attempts to restore the profile to the state it had before the last call. + /// + /// + /// + void RedoUpdateProfile(Profile selectedProfile, ProfileModule module); } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 1f84a08a5..f0dd82bb5 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -7,7 +7,9 @@ using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.LayerBrush; using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Storage.Interfaces; +using Artemis.Storage.Entities.Profile; using Artemis.Storage.Repositories.Interfaces; +using Newtonsoft.Json; namespace Artemis.Core.Services.Storage { @@ -92,6 +94,10 @@ namespace Artemis.Core.Services.Storage public void UpdateProfile(Profile profile, bool includeChildren) { + var memento = JsonConvert.SerializeObject(profile.ProfileEntity); + profile.RedoStack.Clear(); + profile.UndoStack.Push(memento); + profile.ApplyToEntity(); if (includeChildren) { @@ -104,6 +110,34 @@ namespace Artemis.Core.Services.Storage _profileRepository.Save(profile.ProfileEntity); } + public void UndoUpdateProfile(Profile profile, ProfileModule module) + { + if (!profile.UndoStack.Any()) + return; + + ActivateProfile(module, null); + var top = profile.UndoStack.Pop(); + var memento = JsonConvert.SerializeObject(profile.ProfileEntity); + profile.RedoStack.Push(memento); + profile.ProfileEntity = JsonConvert.DeserializeObject(top); + profile.ApplyToProfile(); + ActivateProfile(module, profile); + } + + public void RedoUpdateProfile(Profile profile, ProfileModule module) + { + if (!profile.RedoStack.Any()) + return; + + ActivateProfile(module, null); + var top = profile.RedoStack.Pop(); + var memento = JsonConvert.SerializeObject(profile.ProfileEntity); + profile.UndoStack.Push(memento); + profile.ProfileEntity = JsonConvert.DeserializeObject(top); + profile.ApplyToProfile(); + ActivateProfile(module, profile); + } + private void InstantiateProfileLayerBrushes(Profile profile) { var layerBrushProviders = _pluginService.GetPluginsOfType(); diff --git a/src/Artemis.UI/Ninject/Factories/IViewModelFactory.cs b/src/Artemis.UI/Ninject/Factories/IViewModelFactory.cs index aa65d2797..6d9169fe9 100644 --- a/src/Artemis.UI/Ninject/Factories/IViewModelFactory.cs +++ b/src/Artemis.UI/Ninject/Factories/IViewModelFactory.cs @@ -70,6 +70,6 @@ namespace Artemis.UI.Ninject.Factories public interface IPropertyTrackKeyframeViewModelFactory : IViewModelFactory { - PropertyTrackKeyframeViewModel Create(BaseKeyframe keyframe); + PropertyTrackKeyframeViewModel Create(PropertyTrackViewModel propertyTrackViewModel, BaseKeyframe keyframe); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs index f11559423..ea536a889 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyViewModel.cs @@ -51,7 +51,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties { // Either create a new first keyframe or clear all the keyframes if (_keyframesEnabled) - LayerProperty.CreateNewKeyframe(_profileEditorService.CurrentTime); + LayerProperty.CreateNewKeyframe(_profileEditorService.CurrentTime, LayerProperty.GetCurrentValue()); else LayerProperty.ClearKeyframes(); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml index 0b0918fbe..e7438d926 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputView.xaml @@ -15,8 +15,7 @@ Padding="0 -1" materialDesign:ValidationAssist.UsePopup="True" HorizontalAlignment="Left" - Text="{Binding FloatInputValue}" - Cursor="/Resources/aero_drag_ew.cur" /> + Text="{Binding FloatInputValue}" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml index b7d6bc8f1..7cffca75a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputView.xaml @@ -15,8 +15,7 @@ Padding="0 -1" materialDesign:ValidationAssist.UsePopup="True" HorizontalAlignment="Left" - Text="{Binding IntInputValue}" - Cursor="/Resources/aero_drag_ew.cur" /> + Text="{Binding IntInputValue}"/> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs index 6ccfbefa0..223fe5f2a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs @@ -23,7 +23,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P protected object InputValue { - get => LayerPropertyViewModel.LayerProperty.KeyframeEngine.GetCurrentValue(); + get => LayerPropertyViewModel.LayerProperty.GetCurrentValue(); set => UpdateInputValue(value); } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml index 815b7c16e..2a786dd17 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputView.xaml @@ -16,8 +16,7 @@ materialDesign:ValidationAssist.UsePopup="True" HorizontalAlignment="Left" ToolTip="X-coordinate (horizontal)" - Text="{Binding X}" - Cursor="/Resources/aero_drag_ew.cur" KeyboardNavigation.IsTabStop="True" TabIndex="1" /> + Text="{Binding X}" /> , + Text="{Binding Y}" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml index 94fc68a93..027425f2a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputView.xaml @@ -16,8 +16,7 @@ materialDesign:ValidationAssist.UsePopup="True" HorizontalAlignment="Left" ToolTip="Height" - Text="{Binding Height}" - Cursor="/Resources/aero_drag_ew.cur" /> + Text="{Binding Height}"/> , + Text="{Binding Width}" /> \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs index 3690e89e4..800a34045 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs @@ -14,15 +14,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline private readonly IProfileEditorService _profileEditorService; private int _pixelsPerSecond; - public PropertyTrackKeyframeViewModel(BaseKeyframe keyframe, IProfileEditorService profileEditorService) + public PropertyTrackKeyframeViewModel(PropertyTrackViewModel propertyTrackViewModel, BaseKeyframe keyframe, IProfileEditorService profileEditorService) { _profileEditorService = profileEditorService; + PropertyTrackViewModel = propertyTrackViewModel; Keyframe = keyframe; EasingViewModels = new BindableCollection(); CreateEasingViewModels(); } + public bool IsSelected { get; set; } + public PropertyTrackViewModel PropertyTrackViewModel { get; } public BaseKeyframe Keyframe { get; } public BindableCollection EasingViewModels { get; set; } public double X { get; set; } @@ -49,6 +52,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline public void KeyframeMouseUp(object sender, MouseButtonEventArgs e) { ((IInputElement) sender).ReleaseMouseCapture(); + _profileEditorService.UpdateSelectedProfileElement(); } public void KeyframeMouseMove(object sender, MouseEventArgs e) @@ -72,7 +76,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline Keyframe.Position = newTime; Update(_pixelsPerSecond); - _profileEditorService.UpdateSelectedProfileElement(); + _profileEditorService.UpdateProfilePreview(); return; } @@ -85,12 +89,29 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline Keyframe.Position = newTime; Update(_pixelsPerSecond); - _profileEditorService.UpdateSelectedProfileElement(); + _profileEditorService.UpdateProfilePreview(); } } #endregion + #region Context menu actions + + public void Copy() + { + var keyframe = PropertyTrackViewModel.LayerPropertyViewModel.LayerProperty.CreateNewKeyframe(Keyframe.Position, Keyframe.BaseValue); + keyframe.EasingFunction = Keyframe.EasingFunction; + _profileEditorService.UpdateSelectedProfileElement(); + } + + public void Delete() + { + PropertyTrackViewModel.LayerPropertyViewModel.LayerProperty.RemoveKeyframe(Keyframe); + _profileEditorService.UpdateSelectedProfileElement(); + } + + #endregion + #region Easing private void CreateEasingViewModels() diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackView.xaml index a1358120a..c86f4cd8e 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackView.xaml @@ -39,7 +39,17 @@ MouseMove="{s:Action KeyframeMouseMove}"> - + + + + + + + + + + +