diff --git a/src/Artemis.Core/Models/Profile/Timeline.cs b/src/Artemis.Core/Models/Profile/Timeline.cs index 727ef3109..9c1fc13a7 100644 --- a/src/Artemis.Core/Models/Profile/Timeline.cs +++ b/src/Artemis.Core/Models/Profile/Timeline.cs @@ -12,7 +12,7 @@ namespace Artemis.Core public class Timeline : CorePropertyChanged, IStorageModel { private const int MaxExtraTimelines = 15; - private readonly object _lock = new(); + private readonly object _lock = new (); /// /// Creates a new instance of the class @@ -306,6 +306,8 @@ namespace Artemis.Core #region Updating + private TimeSpan _lastOverridePosition; + /// /// Updates the timeline, applying the provided to the /// @@ -317,9 +319,11 @@ namespace Artemis.Core { Delta += delta; Position += delta; - IsOverridden = false; - if (stickToMainSegment && Position >= MainSegmentEndPosition) + IsOverridden = false; + _lastOverridePosition = Position; + + if (stickToMainSegment && Position > MainSegmentEndPosition) { // If the main segment has no length, simply stick to the start of the segment if (MainSegmentLength == TimeSpan.Zero) @@ -389,14 +393,23 @@ namespace Artemis.Core { lock (_lock) { - Delta += position - Position; + Delta += position - _lastOverridePosition; Position = position; + IsOverridden = true; + _lastOverridePosition = position; if (stickToMainSegment && Position >= MainSegmentStartPosition) { + bool atSegmentStart = Position == MainSegmentStartPosition; if (MainSegmentLength > TimeSpan.Zero) + { Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(Position.TotalMilliseconds % MainSegmentLength.TotalMilliseconds); + // If the cursor is at the end of the timeline we don't want to wrap back around yet so only allow going to the start if the cursor + // is actually at the start of the segment + if (Position == MainSegmentStartPosition && !atSegmentStart) + Position = MainSegmentEndPosition; + } else Position = MainSegmentStartPosition; } diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs index e31950e37..ee678f4fc 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -85,10 +85,7 @@ namespace Artemis.UI.Shared.Services set { if (_currentTime.Equals(value)) return; - if (SelectedProfileElement != null && value > SelectedProfileElement.Timeline.Length) - _currentTime = SelectedProfileElement.Timeline.Length; - else - _currentTime = value; + _currentTime = value; UpdateProfilePreview(); OnCurrentTimeChanged(); } @@ -188,9 +185,9 @@ namespace Artemis.UI.Shared.Services // Stick to the main segment for any element that is not currently selected foreach (Folder folder in SelectedProfile.GetAllFolders()) - folder.Timeline.Override(CurrentTime, folder != SelectedProfileElement && folder.Timeline.PlayMode == TimelinePlayMode.Repeat); + folder.Timeline.Override(CurrentTime, folder.Timeline.PlayMode == TimelinePlayMode.Repeat); foreach (Layer layer in SelectedProfile.GetAllLayers()) - layer.Timeline.Override(CurrentTime, layer != SelectedProfileElement && layer.Timeline.PlayMode == TimelinePlayMode.Repeat); + layer.Timeline.Override(CurrentTime, (layer != SelectedProfileElement || layer.Timeline.Length < CurrentTime) && layer.Timeline.PlayMode == TimelinePlayMode.Repeat); _coreService.FrameRendered += CoreServiceOnFrameRendered; } @@ -309,7 +306,7 @@ namespace Artemis.UI.Shared.Services public bool CanCreatePropertyInputViewModel(ILayerProperty layerProperty) { PropertyInputRegistration? registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == layerProperty.PropertyType); - if (registration == null && layerProperty.PropertyType.IsEnum) + if (registration == null && layerProperty.PropertyType.IsEnum) registration = RegisteredPropertyEditors.FirstOrDefault(r => r.SupportedType == typeof(Enum)); return registration != null; diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml index aeabbaf8b..aaf294c50 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesView.xaml @@ -127,8 +127,12 @@ - Don't repeat the timeline - This setting only applies to the editor and affect the repeat mode during profile use + + Don't repeat the timeline + + + This setting only applies to the editor and affect the repeat mode during normal profile playback + @@ -223,6 +227,18 @@ + + + - - - - diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 527d2907c..4c4088d79 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -486,7 +486,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties public void GoToEnd() { - ProfileEditorService.CurrentTime = CalculateEndTime(); + ProfileEditorService.CurrentTime = SelectedProfileElement.Timeline.EndSegmentEndPosition; } public void GoToPreviousFrame() @@ -500,7 +500,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties { double frameTime = 1000.0 / SettingsService.GetSetting("Core.TargetFrameRate", 25).Value; double newTime = Math.Round((ProfileEditorService.CurrentTime.TotalMilliseconds + frameTime) / frameTime) * frameTime; - newTime = Math.Min(newTime, CalculateEndTime().TotalMilliseconds); + newTime = Math.Min(newTime, SelectedProfileElement.Timeline.EndSegmentEndPosition.TotalMilliseconds); ProfileEditorService.CurrentTime = TimeSpan.FromMilliseconds(newTime); } @@ -524,18 +524,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties Repeating = false; } } - - private TimeSpan CalculateEndTime() - { - List keyframeViewModels = Items.SelectMany(g => g.GetAllKeyframeViewModels(false)).ToList(); - - // If there are no keyframes, don't stop at all - if (!keyframeViewModels.Any()) - return TimeSpan.MaxValue; - // If there are keyframes, stop after the last keyframe + 10 sec - return keyframeViewModels.Max(k => k.Position).Add(TimeSpan.FromSeconds(10)); - } - + private TimeSpan GetCurrentSegmentStart() { TimeSpan current = ProfileEditorService.CurrentTime; @@ -642,6 +631,24 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties } } + public void TimelineJump(object sender, MouseButtonEventArgs e) + { + // Get the parent grid, need that for our position + IInputElement parent = (IInputElement)VisualTreeHelper.GetParent((DependencyObject)sender); + double x = Math.Max(0, e.GetPosition(parent).X); + TimeSpan newTime = TimeSpan.FromSeconds(x / ProfileEditorService.PixelsPerSecond); + + // Round the time to something that fits the current zoom level + if (ProfileEditorService.PixelsPerSecond < 200) + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 5.0) * 5.0); + else if (ProfileEditorService.PixelsPerSecond < 500) + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds / 2.0) * 2.0); + else + newTime = TimeSpan.FromMilliseconds(Math.Round(newTime.TotalMilliseconds)); + + ProfileEditorService.CurrentTime = newTime; + } + #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Controls/PropertyTimelineHeader.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Controls/PropertyTimelineHeader.cs index 8c2204420..bc132c194 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Controls/PropertyTimelineHeader.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/Timeline/Controls/PropertyTimelineHeader.cs @@ -8,7 +8,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Controls { public class PropertyTimelineHeader : FrameworkElement { - public static readonly DependencyProperty FillProperty = DependencyProperty.Register(nameof(Fill), typeof(Brush), typeof(PropertyTimelineHeader), + public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(nameof(Foreground), typeof(Brush), typeof(PropertyTimelineHeader), + new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender)); + + public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(nameof(Background), typeof(Brush), typeof(PropertyTimelineHeader), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender)); public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register(nameof(FontFamily), typeof(FontFamily), typeof(PropertyTimelineHeader), @@ -30,10 +33,16 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Controls private double _subd2; private double _subd3; - public Brush Fill + public Brush Foreground { - get => (Brush) GetValue(FillProperty); - set => SetValue(FillProperty, value); + get => (Brush) GetValue(ForegroundProperty); + set => SetValue(ForegroundProperty, value); + } + + public Brush Background + { + get => (Brush) GetValue(BackgroundProperty); + set => SetValue(BackgroundProperty, value); } public FontFamily FontFamily @@ -71,7 +80,10 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Controls base.OnRender(drawingContext); UpdateTimeScale(); - Pen linePen = new(Fill, 1); + if (Background != null) + drawingContext.DrawRectangle(Background, null, new Rect(0,0, ActualWidth, 30)); + + Pen linePen = new(Foreground, 1); double width = HorizontalOffset + VisibleWidth; int frameStart = 0; @@ -127,7 +139,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.Timeline.Controls private void RenderLabel(DrawingContext drawingContext, string text, double x) { Typeface typeFace = new(FontFamily, new FontStyle(), new FontWeight(), new FontStretch()); - FormattedText formattedText = new(text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeFace, 9, Fill, null, VisualTreeHelper.GetDpi(this).PixelsPerDip); + FormattedText formattedText = new(text, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeFace, 9, Foreground, null, VisualTreeHelper.GetDpi(this).PixelsPerDip); if (x == 0 && OffsetFirstValue) drawingContext.DrawText(formattedText, new Point(2, 5)); else