mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Profile editor - Reimplemented more of the timeline
This commit is contained in:
1 parent
56ba44dbce
commit
a28b2fe2ea
24 files changed
+294
-296
No files matched your search
+2
-2
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
-13
@@ -7,7 +7,7 @@ using Stylet;
|
||||
|
||||
namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
{
|
||||
public class TimelineKeyframeViewModel<T> : Screen, IDisposable
|
||||
public class TimelineKeyframeViewModel<T> : 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<T> 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<T> 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<T>(
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+29
-3
@@ -21,9 +21,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline
|
||||
LayerPropertyViewModel = layerPropertyViewModel;
|
||||
}
|
||||
|
||||
public List<TimeSpan> GetAllKeyframePositions()
|
||||
public List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels()
|
||||
{
|
||||
return LayerProperty.Keyframes.Select(k => k.Position).ToList();
|
||||
return Items.Cast<ITimelineKeyframeViewModel>().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<TimeSpan> GetAllKeyframePositions();
|
||||
List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels();
|
||||
void WipeKeyframes(TimeSpan? start, TimeSpan? end);
|
||||
void ShiftKeyframes(TimeSpan? start, TimeSpan? end, TimeSpan amount);
|
||||
}
|
||||
}
|
||||
+24
-12
@@ -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<LayerPropertyGroupViewModel> 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<LayerPropertyGroupViewModel> 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
|
||||
|
||||
+14
-45
@@ -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<TimelineKeyframeViewModel>((Visual) sender, SelectionRectangle);
|
||||
var selectedKeyframes = HitTestUtilities.GetHitViewModels<ITimelineKeyframeViewModel>((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<TimelineKeyframeViewModel> GetAllKeyframeViewModels()
|
||||
private List<ITimelineKeyframeViewModel> GetAllKeyframeViewModels()
|
||||
{
|
||||
var viewModels = new List<LayerPropertyBaseViewModel>();
|
||||
var viewModels = new List<ITimelineKeyframeViewModel>();
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user