Profile editor - Implemented timeline segments

Profile editor - Added snapping to multiple elements of the timeline
This commit is contained in:
SpoinkyNL committed 2020-07-18 23:52:42 +02:00
1 parent 77dd804e74
commit 732ee2f429
17 files changed
+527 -154

No files matched your search

@@ -145,10 +145,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
subds = new object[] {6d, 6d, 30d};
else if (PixelsPerSecond > 150)
subds = new object[] {4d, 4d, 20d};
else if (PixelsPerSecond > 100)
subds = new object[] {4d, 4d, 8d};
else if (PixelsPerSecond > 140)
subds = new object[] {4d, 4d, 20d};
else if (PixelsPerSecond > 90)
subds = new object[] {4d, 4d, 8d};
subds = new object[] {2d, 4d, 20d};
else if (PixelsPerSecond > 60)
subds = new object[] {2d, 4d, 8d};
else if (PixelsPerSecond > 40)
@@ -119,29 +119,51 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
#region Movement
private bool _movementReleased = true;
private TimeSpan _startOffset;
private TimeSpan? _offset;
public void ApplyMovement(TimeSpan cursorTime)
{
if (_movementReleased)
{
_movementReleased = false;
_startOffset = cursorTime - BaseLayerPropertyKeyframe.Position;
}
else
{
BaseLayerPropertyKeyframe.Position = cursorTime - _startOffset;
if (BaseLayerPropertyKeyframe.Position < TimeSpan.Zero)
BaseLayerPropertyKeyframe.Position = TimeSpan.Zero;
Update(_pixelsPerSecond);
}
UpdatePosition(cursorTime);
Update(_pixelsPerSecond);
}
public void ReleaseMovement()
{
_movementReleased = true;
_offset = null;
}
public void SaveOffsetToKeyframe(TimelineKeyframeViewModel keyframeViewModel)
{
if (keyframeViewModel == this)
{
_offset = null;
return;
}
if (_offset != null)
return;
_offset = BaseLayerPropertyKeyframe.Position - keyframeViewModel.BaseLayerPropertyKeyframe.Position;
}
public void ApplyOffsetToKeyframe(TimelineKeyframeViewModel keyframeViewModel)
{
if (keyframeViewModel == this || _offset == null)
return;
UpdatePosition(keyframeViewModel.BaseLayerPropertyKeyframe.Position + _offset.Value);
}
private void UpdatePosition(TimeSpan position)
{
if (position < TimeSpan.Zero)
BaseLayerPropertyKeyframe.Position = TimeSpan.Zero;
else if (position > _profileEditorService.SelectedProfileElement.TimelineLength)
BaseLayerPropertyKeyframe.Position = _profileEditorService.SelectedProfileElement.TimelineLength;
else
BaseLayerPropertyKeyframe.Position = position;
Update(_pixelsPerSecond);
}
#endregion
@@ -13,7 +13,8 @@
Background="{DynamicResource MaterialDesignToolBarBackground}"
MouseDown="{s:Action TimelineCanvasMouseDown}"
MouseUp="{s:Action TimelineCanvasMouseUp}"
MouseMove="{s:Action TimelineCanvasMouseMove}">
MouseMove="{s:Action TimelineCanvasMouseMove}"
Margin="0 0 -1 0">
<Grid.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseLeftButtonDown">
<BeginStoryboard>
@@ -32,7 +33,7 @@
</Grid.Triggers>
<ItemsControl ItemsSource="{Binding LayerPropertyGroups}"
MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ScrollViewer}}"
MinWidth="{Binding TotalTimelineWidth}"
HorizontalAlignment="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
@@ -48,7 +49,8 @@
X2="{Binding StartSegmentEndPosition}"
Y1="0"
Y2="{Binding ActualHeight, ElementName=TimelineContainerGrid}"
HorizontalAlignment="Left" />
HorizontalAlignment="Left"
Visibility="{Binding StartSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"/>
<Line Stroke="{StaticResource PrimaryHueDarkBrush}"
Opacity="0.5"
StrokeDashArray="4 2"
@@ -62,7 +64,8 @@
X1="{Binding EndSegmentEndPosition}"
X2="{Binding EndSegmentEndPosition}"
Y1="0"
Y2="{Binding ActualHeight, ElementName=TimelineContainerGrid}" />
Y2="{Binding ActualHeight, ElementName=TimelineContainerGrid}"
Visibility="{Binding EndSegmentEnabled, Converter={x:Static s:BoolToVisibilityConverter.Instance}}"/>
<!-- Multi-selection rectangle -->
<Path x:Name="MultiSelectionPath"
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
@@ -27,6 +28,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
LayerPropertyGroups = layerPropertyGroups;
SelectionRectangle = new RectangleGeometry();
_profileEditorService.SelectedProfileElement.PropertyChanged += SelectedProfileElementOnPropertyChanged;
_profileEditorService.PixelsPerSecondChanged += ProfileEditorServiceOnPixelsPerSecondChanged;
Update();
@@ -46,10 +48,42 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public double MainSegmentEndPosition => StartSegmentWidth + MainSegmentWidth;
public double EndSegmentWidth => _profileEditorService.PixelsPerSecond * _profileEditorService.SelectedProfileElement?.EndSegmentLength.TotalSeconds ?? 0;
public double EndSegmentEndPosition => StartSegmentWidth + MainSegmentWidth + EndSegmentWidth;
public double TotalTimelineWidth => _profileEditorService.PixelsPerSecond * _profileEditorService.SelectedProfileElement?.TimelineLength.TotalSeconds ?? 0;
public bool StartSegmentEnabled => _profileEditorService.SelectedProfileElement?.StartSegmentLength != TimeSpan.Zero;
public bool EndSegmentEnabled => _profileEditorService.SelectedProfileElement?.EndSegmentLength != TimeSpan.Zero;
public void Dispose()
{
_profileEditorService.PixelsPerSecondChanged -= ProfileEditorServiceOnPixelsPerSecondChanged;
_profileEditorService.SelectedProfileElement.PropertyChanged -= SelectedProfileElementOnPropertyChanged;
}
private void SelectedProfileElementOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(_profileEditorService.SelectedProfileElement.StartSegmentLength))
{
NotifyOfPropertyChange(nameof(StartSegmentWidth));
NotifyOfPropertyChange(nameof(StartSegmentEndPosition));
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
NotifyOfPropertyChange(nameof(StartSegmentEnabled));
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
}
else if (e.PropertyName == nameof(_profileEditorService.SelectedProfileElement.MainSegmentLength))
{
NotifyOfPropertyChange(nameof(MainSegmentWidth));
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
}
else if (e.PropertyName == nameof(_profileEditorService.SelectedProfileElement.EndSegmentLength))
{
NotifyOfPropertyChange(nameof(EndSegmentWidth));
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
NotifyOfPropertyChange(nameof(EndSegmentEnabled));
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
}
}
public void Update()
@@ -74,6 +108,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
NotifyOfPropertyChange(nameof(MainSegmentEndPosition));
NotifyOfPropertyChange(nameof(EndSegmentWidth));
NotifyOfPropertyChange(nameof(EndSegmentEndPosition));
NotifyOfPropertyChange(nameof(TotalTimelineWidth));
}
#region Command handlers
@@ -108,8 +143,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
public void KeyframeMouseMove(object sender, MouseEventArgs e)
{
var viewModel = (sender as Ellipse)?.DataContext as TimelineKeyframeViewModel;
if (viewModel == null)
return;
if (e.LeftButton == MouseButtonState.Pressed)
MoveSelectedKeyframes(GetCursorTime(e.GetPosition(View)));
MoveSelectedKeyframes(GetCursorTime(e.GetPosition(View)), viewModel);
e.Handled = true;
}
@@ -161,6 +200,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
var tolerance = 1000f / _profileEditorService.PixelsPerSecond * 5;
if (Math.Abs(_profileEditorService.CurrentTime.TotalMilliseconds - time.TotalMilliseconds) < tolerance)
time = _profileEditorService.CurrentTime;
else if (Math.Abs(_profileEditorService.SelectedProfileElement.StartSegmentLength.TotalMilliseconds - time.TotalMilliseconds) < tolerance)
time = _profileEditorService.SelectedProfileElement.StartSegmentLength;
}
return time;
@@ -170,14 +211,19 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline
#region Keyframe movement
public void MoveSelectedKeyframes(TimeSpan cursorTime)
public void MoveSelectedKeyframes(TimeSpan cursorTime, TimelineKeyframeViewModel sourceKeyframeViewModel)
{
// Ensure the selection rectangle doesn't show, the view isn't aware of different types of dragging
SelectionRectangle.Rect = new Rect();
var keyframeViewModels = GetAllKeyframeViewModels();
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
keyframeViewModel.ApplyMovement(cursorTime);
keyframeViewModel.SaveOffsetToKeyframe(sourceKeyframeViewModel);
sourceKeyframeViewModel.ApplyMovement(cursorTime);
foreach (var keyframeViewModel in keyframeViewModels.Where(k => k.IsSelected))
keyframeViewModel.ApplyOffsetToKeyframe(sourceKeyframeViewModel);
_layerPropertiesViewModel.ProfileEditorService.UpdateProfilePreview();
}