diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index 20c938211..4c735e94d 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -204,8 +204,7 @@ namespace Artemis.Core return (TimelinePosition - oldPosition).TotalSeconds; } - - + /// /// Overrides the progress of the element /// diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs index b4185660e..ce1f49197 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs @@ -84,10 +84,9 @@ namespace Artemis.UI.Shared.Services /// How close the time must be to snap /// Enable snapping to timeline segments /// Enable snapping to the current time of the editor - /// Enable snapping to visible keyframes - /// A keyframe to exclude during keyframe snapping + /// An optional extra list of times to snap to /// - TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null); + TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, List snapTimes = null); /// /// If a matching registration is found, creates a new supporting diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs index bb651f9bd..23004586a 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs @@ -35,7 +35,7 @@ namespace Artemis.UI.Shared.Services public IReadOnlyList RegisteredPropertyEditors => _registeredPropertyEditors.AsReadOnly(); public Profile SelectedProfile { get; private set; } public RenderProfileElement SelectedProfileElement { get; private set; } - public BaseLayerProperty SelectedDataBinding { get; private set; } + public ILayerProperty SelectedDataBinding { get; private set; } public TimeSpan CurrentTime { @@ -128,7 +128,7 @@ namespace Artemis.UI.Shared.Services } } - public void ChangeSelectedDataBinding(BaseLayerProperty layerProperty) + public void ChangeSelectedDataBinding(ILayerProperty layerProperty) { SelectedDataBinding = layerProperty; OnSelectedDataBindingChanged(); @@ -229,7 +229,7 @@ namespace Artemis.UI.Shared.Services } } - public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, bool snapToKeyframes, BaseLayerPropertyKeyframe excludedKeyframe = null) + public TimeSpan SnapToTimeline(TimeSpan time, TimeSpan tolerance, bool snapToSegments, bool snapToCurrentTime, List snapTimes = null) { if (snapToSegments) { @@ -254,17 +254,12 @@ namespace Artemis.UI.Shared.Services return SelectedProfileElement.StartSegmentLength; } - if (snapToKeyframes) + if (snapTimes != null) { - // Get all visible keyframes - var keyframes = SelectedProfileElement.GetAllKeyframes() - .Where(k => k != excludedKeyframe && SelectedProfileElement.IsPropertyGroupExpanded(k.BaseLayerProperty.Parent)) - .ToList(); - // Find the closest keyframe - var closeKeyframe = keyframes.FirstOrDefault(k => Math.Abs(time.TotalMilliseconds - k.Position.TotalMilliseconds) < tolerance.TotalMilliseconds); - if (closeKeyframe != null) - return closeKeyframe.Position; + var closeSnapTime = snapTimes.FirstOrDefault(s => Math.Abs(time.TotalMilliseconds - s.TotalMilliseconds) < tolerance.TotalMilliseconds); + if (closeSnapTime != TimeSpan.Zero) + return closeSnapTime; } return time; diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 22467b5ac..dd0743d08 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -16,6 +16,7 @@ using Artemis.UI.Shared; using Artemis.UI.Shared.Services; using GongSolutions.Wpf.DragDrop; using Stylet; +using static Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree.LayerPropertyGroupTreeViewModel.LayerPropertyGroupType; namespace Artemis.UI.Screens.ProfileEditor.LayerProperties { @@ -38,7 +39,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties private TimelineViewModel _timelineViewModel; private TreeViewModel _treeViewModel; - public LayerPropertiesViewModel(IProfileEditorService profileEditorService, + public LayerPropertiesViewModel(IProfileEditorService profileEditorService, ICoreService coreService, ISettingsService settingsService, ILayerPropertyVmFactory layerPropertyVmFactory, @@ -397,9 +398,14 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties private void SortProperties() { // Get all non-effect properties - var nonEffectProperties = LayerPropertyGroups.Where(l => l.GroupType != LayerEffectRoot).ToList(); + var nonEffectProperties = LayerPropertyGroups + .Where(l => l.LayerPropertyGroupTreeViewModel.GroupType != LayerEffectRoot) + .ToList(); // Order the effects - var effectProperties = LayerPropertyGroups.Where(l => l.GroupType == LayerEffectRoot).OrderBy(l => l.LayerPropertyGroup.LayerEffect.Order).ToList(); + var effectProperties = LayerPropertyGroups + .Where(l => l.LayerPropertyGroupTreeViewModel.GroupType == LayerEffectRoot) + .OrderBy(l => l.LayerPropertyGroup.LayerEffect.Order) + .ToList(); // Put the non-effect properties in front for (var index = 0; index < nonEffectProperties.Count; index++) @@ -435,7 +441,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties var source = dropInfo.Data as LayerPropertyGroupViewModel; var target = dropInfo.TargetItem as LayerPropertyGroupViewModel; - if (source == target || target?.GroupType != LayerEffectRoot || source?.GroupType != LayerEffectRoot) + if (source == target || + target?.LayerPropertyGroupTreeViewModel.GroupType != LayerEffectRoot || + source?.LayerPropertyGroupTreeViewModel.GroupType != LayerEffectRoot) return; dropInfo.DropTargetAdorner = DropTargetAdorners.Insert; @@ -452,7 +460,9 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties var source = dropInfo.Data as LayerPropertyGroupViewModel; var target = dropInfo.TargetItem as LayerPropertyGroupViewModel; - if (source == target || target?.GroupType != LayerEffectRoot || source?.GroupType != LayerEffectRoot) + if (source == target || + target?.LayerPropertyGroupTreeViewModel.GroupType != LayerEffectRoot || + source?.LayerPropertyGroupTreeViewModel.GroupType != LayerEffectRoot) return; if (dropInfo.InsertPosition == RelativeInsertPosition.BeforeTargetItem) @@ -481,7 +491,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties private void ApplyCurrentEffectsOrder() { var order = 1; - foreach (var groupViewModel in LayerPropertyGroups.Where(p => p.GroupType == LayerEffectRoot)) + foreach (var groupViewModel in LayerPropertyGroups.Where(p => p.LayerPropertyGroupTreeViewModel.GroupType == LayerEffectRoot)) { groupViewModel.UpdateOrder(order); order++; @@ -551,16 +561,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties private TimeSpan CalculateEndTime() { - if (!(ProfileEditorService.SelectedProfileElement is Layer layer)) - return TimeSpan.MaxValue; - - var keyframes = GetKeyframes(false); + var keyframeTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframePositions(false)).ToList(); // If there are no keyframes, don't stop at all - if (!keyframes.Any()) + if (!keyframeTimes.Any()) return TimeSpan.MaxValue; // If there are keyframes, stop after the last keyframe + 10 sec - return keyframes.Max(k => k.Position).Add(TimeSpan.FromSeconds(10)); + return keyframeTimes.Max().Add(TimeSpan.FromSeconds(10)); } private void CoreServiceOnFrameRendering(object sender, FrameRenderingEventArgs e) @@ -617,7 +624,8 @@ 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 snappedTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), true, false, true); + var snapTimes = LayerPropertyGroups.SelectMany(g => g.GetAllKeyframePositions(true)).ToList(); + var snappedTime = ProfileEditorService.SnapToTimeline(newTime, TimeSpan.FromMilliseconds(1000f / ProfileEditorService.PixelsPerSecond * 5), true, false, snapTimes); ProfileEditorService.CurrentTime = snappedTime; return; } @@ -634,15 +642,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties } } - private List GetKeyframes(bool visibleOnly) - { - var result = new List(); - foreach (var layerPropertyGroupViewModel in LayerPropertyGroups) - result.AddRange(layerPropertyGroupViewModel.GetKeyframes(visibleOnly)); - - return result; - } - #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs index 3f87b3479..ae3e7091a 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Artemis.Core; using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.ProfileEditor.LayerProperties.Tree; @@ -69,5 +70,28 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties disposableChild.Dispose(); } } + + public void UpdateOrder(int order) + { + LayerPropertyGroup.LayerEffect.Order = order; + NotifyOfPropertyChange(nameof(IsExpanded)); + } + + public List GetAllKeyframePositions(bool expandedOnly) + { + var result = new List(); + if (expandedOnly == IsExpanded) + return result; + + foreach (var child in Children) + { + if (child is LayerPropertyViewModel layerPropertyViewModel) + result.AddRange(layerPropertyViewModel.LayerPropertyTimelineViewModel.GetAllKeyframePositions()); + else if (child is LayerPropertyGroupViewModel layerPropertyGroupViewModel) + result.AddRange(layerPropertyGroupViewModel.GetAllKeyframePositions(expandedOnly)); + } + + return result; + } } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Rails/LayerPropertyTimelineViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Rails/LayerPropertyTimelineViewModel.cs index 370dc0904..36a6e00c8 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Rails/LayerPropertyTimelineViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Rails/LayerPropertyTimelineViewModel.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Artemis.Core; using Stylet; @@ -15,6 +17,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties LayerPropertyViewModel = layerPropertyViewModel; } + public List GetAllKeyframePositions() + { + return LayerProperty.Keyframes.Select(k => k.Position).ToList(); + } + public void Dispose() { } @@ -22,5 +29,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties public interface ILayerPropertyTimelineViewModel : IScreen, IDisposable { + List GetAllKeyframePositions(); } } \ No newline at end of file