From 7a7fffeb3dd5c0fd28b31abffb0677aebd54a2e5 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 18 Feb 2020 23:27:38 +0100 Subject: [PATCH] Profile editor - Improved profile loading and UI population Layer properties - Added drag-editing to float input (to be expanded to all) --- src/Artemis.Core/Services/CoreService.cs | 2 +- .../Behaviors/TreeViewSelectionBehavior.cs | 2 +- .../LayerPropertiesViewModel.cs | 6 +- .../PropertyInput/BrushPropertyInputView.xaml | 5 +- .../BrushPropertyInputViewModel.cs | 5 ++ .../PropertyInput/EnumPropertyInputView.xaml | 5 +- .../EnumPropertyInputViewModel.cs | 5 ++ .../PropertyInput/FloatPropertyInputView.xaml | 33 +++++++- .../FloatPropertyInputViewModel.cs | 7 ++ .../PropertyInput/IntPropertyInputView.xaml | 7 +- .../IntPropertyInputViewModel.cs | 5 ++ .../PropertyInput/PropertyInputViewModel.cs | 83 ++++++++++++++++--- .../SKColorPropertyInputView.xaml | 4 +- .../SKColorPropertyInputViewModel.cs | 5 ++ .../SKPointPropertyInputView.xaml | 10 +-- .../SKPointPropertyInputViewModel.cs | 5 ++ .../SKSizePropertyInputView.xaml | 10 +-- .../SKSizePropertyInputViewModel.cs | 5 ++ .../Timeline/PropertyTimelineView.xaml | 54 +++++++++--- .../Timeline/PropertyTimelineViewModel.cs | 74 ++++++++++++++++- .../PropertyTrackKeyframeViewModel.cs | 7 ++ .../ProfileEditor/ProfileEditorViewModel.cs | 21 +++-- .../ProfileTree/ProfileTreeViewModel.cs | 5 +- .../Services/ProfileEditorService.cs | 2 + 24 files changed, 298 insertions(+), 69 deletions(-) diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 80203346f..af70048f2 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -99,7 +99,7 @@ namespace Artemis.Core.Services { try { - if (!ModuleUpdatingDisabled) + if (!ModuleUpdatingDisabled && _modules != null) { lock (_modules) { diff --git a/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs b/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs index 338f12bcf..2c05e51bd 100644 --- a/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs +++ b/src/Artemis.UI/Behaviors/TreeViewSelectionBehavior.cs @@ -97,7 +97,7 @@ namespace Artemis.UI.Behaviors // Update state of all items starting with given, with optional recursion private void UpdateTreeViewItem(TreeViewItem item, bool recurse) { - if (SelectedItem == null) return; +// if (SelectedItem == null) return; var model = item.DataContext; // If the selected item is this model and is not yet selected - select and return diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index b6f1db1ac..4a1d2ca94 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -44,7 +44,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties PopulateProperties(_profileEditorService.SelectedProfileElement, null); _profileEditorService.SelectedProfileElementChanged += (sender, args) => PopulateProperties(args.ProfileElement, args.PreviousProfileElement); - _profileEditorService.SelectedProfileChanged += (sender, args) => PopulateProperties(_profileEditorService.SelectedProfileElement, null); _profileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged; } @@ -112,6 +111,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties layer.LayerPropertyRegistered += LayerOnPropertyRegistered; layer.LayerPropertyRemoved += LayerOnPropertyRemoved; } + else + { + foreach (var layerPropertyViewModel in _layerPropertyViewModels.ToList()) + RemovePropertyViewModel(layerPropertyViewModel); + } } private void LayerOnPropertyRegistered(object sender, LayerPropertyEventArgs e) diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml index 7145487ff..70bb43b15 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml @@ -9,7 +9,7 @@ mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs index 9f4a36c8c..ebdb311d4 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs @@ -59,6 +59,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P NotifyOfPropertyChange(() => BrushInputValue); } + public override void ApplyInputDrag(object startValue, double dragDistance) + { + throw new NotImplementedException(); + } + protected override void OnInitialized() { UpdateEnumValues(); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml index ab41fbd94..489314187 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml @@ -10,7 +10,7 @@ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:EnumPropertyInputViewModel}"> - + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputViewModel.cs index f1e1d7932..738ef2ce3 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputViewModel.cs @@ -27,6 +27,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P NotifyOfPropertyChange(() => EnumInputValue); } + public override void ApplyInputDrag(object startValue, double dragDistance) + { + throw new NotImplementedException(); + } + protected override void OnInitialized() { EnumValues = EnumUtilities.GetAllValuesAndDescriptions(LayerPropertyViewModel.LayerProperty.Type); 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 b283cb6e0..aa28bdda5 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 @@ -10,14 +10,41 @@ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:FloatPropertyInputViewModel}"> - + + + + + + + + + + + + + + - + Visibility="{Binding InputFieldEnabled, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" + LostFocus="{s:Action InputLostFocus}" + KeyDown="{s:Action InputKeyDown}" + IsVisibleChanged="{s:Action InputIsVisibleChanged}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputViewModel.cs index 36fa963fb..f892993a3 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/FloatPropertyInputViewModel.cs @@ -12,6 +12,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P public sealed override List CompatibleTypes { get; } = new List {typeof(float)}; + public float FloatInputValue { get => (float?) InputValue ?? 0f; @@ -22,5 +23,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P { NotifyOfPropertyChange(() => FloatInputValue); } + + public override void ApplyInputDrag(object startValue, double dragDistance) + { + var floatStartValue = (float) startValue; + FloatInputValue = (float) (floatStartValue + dragDistance); + } } } \ 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 72e469a2b..80bc7ecb7 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 @@ -10,14 +10,13 @@ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:IntPropertyInputViewModel}"> - + - + Text="{Binding IntInputValue}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputViewModel.cs index 6f1a890b3..9708a01ed 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/IntPropertyInputViewModel.cs @@ -22,5 +22,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P { NotifyOfPropertyChange(() => IntInputValue); } + + public override void ApplyInputDrag(object startValue, double dragDistance) + { + throw new NotImplementedException(); + } } } \ 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 d1c0db1d2..ac7f00e2e 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 @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; using Artemis.UI.Exceptions; using Artemis.UI.Services.Interfaces; using Stylet; @@ -15,11 +17,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P } protected IProfileEditorService ProfileEditorService { get; set; } + public abstract List CompatibleTypes { get; } public bool Initialized { get; private set; } - - public abstract List CompatibleTypes { get; } public LayerPropertyViewModel LayerPropertyViewModel { get; private set; } + public bool InputFieldEnabled { get; set; } protected object InputValue { @@ -46,18 +48,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P OnInitialized(); } - /// - /// Called by the view, prevents scrolling into view when scrubbing through the timeline - /// - /// - /// - public void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) - { - e.Handled = true; - } - public abstract void Update(); + public abstract void ApplyInputDrag(object startValue, double dragDistance); + protected virtual void OnInitialized() { } @@ -70,5 +64,70 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P ProfileEditorService.UpdateSelectedProfileElement(); } + + #region Mouse-based mutations + + private Point _mouseDragStartPoint; + private object _startValue; + + // ReSharper disable once UnusedMember.Global - Called from view + public void InputMouseDown(object sender, MouseButtonEventArgs e) + { + _startValue = InputValue; + ((IInputElement) sender).CaptureMouse(); + _mouseDragStartPoint = e.GetPosition((IInputElement) sender); + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void InputMouseUp(object sender, MouseEventArgs e) + { + var position = e.GetPosition((IInputElement) sender); + if (position == _mouseDragStartPoint) + InputFieldEnabled = true; + + ((IInputElement) sender).ReleaseMouseCapture(); + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void InputMouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + var position = e.GetPosition((IInputElement) sender); + ApplyInputDrag(_startValue, position.X - _mouseDragStartPoint.X); + } + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void InputIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (InputFieldEnabled) + { + ((UIElement) sender).Focus(); + if (sender is TextBox textBox) + textBox.SelectAll(); + } + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void InputLostFocus(object sender, RoutedEventArgs e) + { + InputFieldEnabled = false; + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void InputKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + InputFieldEnabled = false; + else if (e.Key == Key.Escape) + { + if (sender is TextBox textBox) + textBox.Text = _startValue.ToString(); + InputFieldEnabled = false; + } + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml index 057e78127..5884fc22f 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml @@ -15,11 +15,11 @@ - + - + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs index ed5ca0332..b4223241a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs @@ -23,5 +23,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P { NotifyOfPropertyChange(() => SKColorInputValue); } + + public override void ApplyInputDrag(object startValue, double dragDistance) + { + throw new NotImplementedException(); + } } } \ No newline at end of file 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 d0958778e..2a10df499 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 @@ -10,15 +10,14 @@ d:DesignHeight="25" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:SKPointPropertyInputViewModel}"> - + + Text="{Binding X}" /> , - + Text="{Binding Y}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputViewModel.cs index 31182ef04..0c48e1872 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKPointPropertyInputViewModel.cs @@ -34,5 +34,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P NotifyOfPropertyChange(() => X); NotifyOfPropertyChange(() => Y); } + + public override void ApplyInputDrag(object startValue, double dragDistance) + { + throw new NotImplementedException(); + } } } \ 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 7e3054d4c..127b6219e 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 @@ -10,15 +10,14 @@ d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:SKSizePropertyInputViewModel}"> - + + Text="{Binding Height}" /> , - + Text="{Binding Width}" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputViewModel.cs index 8deb5b199..1722da57a 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKSizePropertyInputViewModel.cs @@ -34,5 +34,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P NotifyOfPropertyChange(() => Width); NotifyOfPropertyChange(() => Height); } + + public override void ApplyInputDrag(object startValue, double dragDistance) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml index 7c92513b7..a3afcc31c 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineView.xaml @@ -9,15 +9,47 @@ d:DesignHeight="25" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:PropertyTimelineViewModel}"> - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs index 5c196db08..1cd074eeb 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTimelineViewModel.cs @@ -1,7 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; +using Artemis.Core.Models.Surface; using Artemis.UI.Ninject.Factories; +using Artemis.UI.Screens.SurfaceEditor; +using Artemis.UI.Screens.SurfaceEditor.Visualization; using Artemis.UI.Services.Interfaces; using Stylet; @@ -24,12 +32,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline _profileEditorService.SelectedProfileElementUpdated += (sender, args) => Update(); LayerPropertiesViewModel.PixelsPerSecondChanged += (sender, args) => UpdateKeyframePositions(); + + Execute.PostToUIThread(() => SelectionRectangle = new RectangleGeometry()); } public LayerPropertiesViewModel LayerPropertiesViewModel { get; } public double Width { get; set; } public BindableCollection PropertyTrackViewModels { get; set; } + public RectangleGeometry SelectionRectangle { get; set; } public void UpdateEndTime() { @@ -88,11 +99,53 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline UpdateEndTime(); } - private void CreateViewModels(LayerPropertyViewModel property) + #region Keyframe selection + + private Point _mouseDragStartPoint; + + // ReSharper disable once UnusedMember.Global - Called from view + public void TimelineCanvasMouseDown(object sender, MouseButtonEventArgs e) { - PropertyTrackViewModels.Add(_propertyTrackVmFactory.Create(this, property)); - foreach (var child in property.Children) - CreateViewModels(child); + ((IInputElement) sender).CaptureMouse(); + + SelectionRectangle.Rect = new Rect(); + _mouseDragStartPoint = e.GetPosition((IInputElement) sender); + } + + // ReSharper disable once UnusedMember.Global - Called from view + public void TimelineCanvasMouseUp(object sender, MouseEventArgs e) + { + var position = e.GetPosition((IInputElement)sender); + var selectedRect = new Rect(_mouseDragStartPoint, position); + SelectionRectangle.Rect = selectedRect; + + // Find all keyframes in the rectangle + var selectedKeyframes = new List(); + var hitTestParams = new GeometryHitTestParameters(SelectionRectangle); + var resultCallback = new HitTestResultCallback(result => HitTestResultBehavior.Continue); + var filterCallback = new HitTestFilterCallback(element => + { + if (element is Ellipse ellipse) + selectedKeyframes.Add((PropertyTrackKeyframeViewModel) ellipse.DataContext); + return HitTestFilterBehavior.Continue; + }); + VisualTreeHelper.HitTest((Visual) sender, filterCallback, resultCallback, hitTestParams); + + var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); + foreach (var keyframeViewModel in keyframeViewModels) + keyframeViewModel.IsSelected = selectedKeyframes.Contains(keyframeViewModel); + + ((IInputElement) sender).ReleaseMouseCapture(); + } + + public void TimelineCanvasMouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + { + var position = e.GetPosition((IInputElement) sender); + var selectedRect = new Rect(_mouseDragStartPoint, position); + SelectionRectangle.Rect = selectedRect; + } } public void SelectKeyframe(PropertyTrackKeyframeViewModel clicked, bool selectBetween, bool toggle) @@ -137,6 +190,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline } } + #endregion + + #region Keyframe movement + public void MoveSelectedKeyframes(TimeSpan offset) { var keyframeViewModels = PropertyTrackViewModels.SelectMany(t => t.KeyframeViewModels.OrderBy(k => k.Keyframe.Position)).ToList(); @@ -152,5 +209,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline _profileEditorService.UpdateProfilePreview(); } + + #endregion + + private void CreateViewModels(LayerPropertyViewModel property) + { + PropertyTrackViewModels.Add(_propertyTrackVmFactory.Create(this, property)); + foreach (var child in property.Children) + CreateViewModels(child); + } } } \ 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 811a49e66..5b2f037c5 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Timeline/PropertyTrackKeyframeViewModel.cs @@ -46,6 +46,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline public void KeyframeMouseDown(object sender, MouseButtonEventArgs e) { + if (e.LeftButton == MouseButtonState.Released) + return; if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift) && !IsSelected) PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, true, false); else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) @@ -54,12 +56,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline PropertyTrackViewModel.PropertyTimelineViewModel.SelectKeyframe(this, false, false); ((IInputElement) sender).CaptureMouse(); + e.Handled = true; } public void KeyframeMouseUp(object sender, MouseButtonEventArgs e) { + if (e.LeftButton == MouseButtonState.Released) + return; ((IInputElement) sender).ReleaseMouseCapture(); _profileEditorService.UpdateSelectedProfileElement(); + e.Handled = true; } public void KeyframeMouseMove(object sender, MouseEventArgs e) @@ -89,6 +95,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline PropertyTrackViewModel.PropertyTimelineViewModel.MoveSelectedKeyframes(newTime - Keyframe.Position); } + e.Handled = true; } #endregion diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs index 20c937d01..0a4aa1f33 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs @@ -136,9 +136,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor private void ChangeSelectedProfile(Profile profile) { - if (profile == Module.ActiveProfile) - return; - var oldProfile = Module.ActiveProfile; _profileService.ActivateProfile(Module, profile); @@ -147,7 +144,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor if (profile != null) _profileService.UpdateProfile(profile, false); - _profileEditorService.ChangeSelectedProfile(profile); + if (_profileEditorService.SelectedProfile != profile) + _profileEditorService.ChangeSelectedProfile(profile); } private void ModuleOnActiveProfileChanged(object sender, EventArgs e) @@ -188,11 +186,18 @@ namespace Artemis.UI.Screens.Module.ProfileEditor // Populate the UI collection Execute.PostToUIThread(() => { - Profiles.Clear(); - Profiles.AddRange(profiles.OrderBy(p => p.Name)); - SelectedProfile = activeProfile; + Profiles.AddRange(profiles.Except(Profiles).ToList()); + Profiles.RemoveRange(Profiles.Except(profiles).ToList()); + var index = 0; + foreach (var profile in Profiles.OrderBy(p => p.Name).ToList()) + { + Profiles.Move(Profiles.IndexOf(profile), index); + index++; + } - _profileEditorService.ChangeSelectedProfile(SelectedProfile); + SelectedProfile = activeProfile; + if (_profileEditorService.SelectedProfile != activeProfile) + _profileEditorService.ChangeSelectedProfile(activeProfile); if (!activeProfile.IsActivated) _profileService.ActivateProfile(Module, activeProfile); }); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs index e6ee1c533..484701d94 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs @@ -16,10 +16,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.ProfileTree private TreeItemViewModel _selectedTreeItem; private bool _updatingTree; - - public ProfileTreeViewModel(IProfileEditorService profileEditorService, - IFolderVmFactory folderVmFactory, - ILayerVmFactory layerVmFactory) + public ProfileTreeViewModel(IProfileEditorService profileEditorService, IFolderVmFactory folderVmFactory) { _profileEditorService = profileEditorService; _folderVmFactory = folderVmFactory; diff --git a/src/Artemis.UI/Services/ProfileEditorService.cs b/src/Artemis.UI/Services/ProfileEditorService.cs index bc976c79b..e9e6262f9 100644 --- a/src/Artemis.UI/Services/ProfileEditorService.cs +++ b/src/Artemis.UI/Services/ProfileEditorService.cs @@ -40,6 +40,8 @@ namespace Artemis.UI.Services public void ChangeSelectedProfile(Profile profile) { + ChangeSelectedProfileElement(null); + var profileElementEvent = new ProfileElementEventArgs(profile, SelectedProfile); SelectedProfile = profile; UpdateProfilePreview();