Browse Source

Simplified slider thumb value and calculation

flabbet 1 year ago
parent
commit
a33a55f45c

+ 1 - 1
src/PixiEditor.AvaloniaUI/Helpers/Converters/TimelineSliderValueToMarginConverter.cs

@@ -19,7 +19,7 @@ internal class TimelineSliderValueToMarginConverter : SingleInstanceMultiValueCo
 
 
         if (values[0] is int frame && values[1] is double scale && values[2] is Vector offset)
         if (values[0] is int frame && values[1] is double scale && values[2] is Vector offset)
         {
         {
-            return new Thickness(frame * scale + offset.X, 0, 0, 0);
+            return new Thickness(frame * scale - offset.X, 0, 0, 0);
         }
         }
 
 
         return new Thickness();
         return new Thickness();

+ 3 - 3
src/PixiEditor.AvaloniaUI/Styles/Templates/Timeline.axaml

@@ -85,8 +85,8 @@
                             </Interaction.Behaviors>
                             </Interaction.Behaviors>
                         </animations:TimelineSlider>
                         </animations:TimelineSlider>
 
 
-                        <Panel Grid.Row="1" Grid.Column="1" Margin="29, -16, 0, 0" VerticalAlignment="Stretch"
-                               ZIndex="11" HorizontalAlignment="Left">
+                        <Panel ClipToBounds="True" Grid.Row="1" Grid.Column="1" Margin="29, -16, 0, 0" VerticalAlignment="Stretch"
+                               ZIndex="11" HorizontalAlignment="Left" IsHitTestVisible="False">
                             <Border Width="2" Background="{DynamicResource ThemeAccentBrush}">
                             <Border Width="2" Background="{DynamicResource ThemeAccentBrush}">
                                 <Border.Margin>
                                 <Border.Margin>
                                     <MultiBinding Converter="{converters:TimelineSliderValueToMarginConverter}">
                                     <MultiBinding Converter="{converters:TimelineSliderValueToMarginConverter}">
@@ -117,7 +117,7 @@
                             </StackPanel>
                             </StackPanel>
                         </ScrollViewer>
                         </ScrollViewer>
                         <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
                         <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
-                                      Grid.Row="1"
+                                      Grid.Row="1" Offset="{Binding ScrollOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                                       Name="PART_TimelineKeyFramesScroll" Grid.Column="1">
                                       Name="PART_TimelineKeyFramesScroll" Grid.Column="1">
                             <Border Background="{DynamicResource ThemeBackgroundBrush}" Name="PART_ContentBorder">
                             <Border Background="{DynamicResource ThemeBackgroundBrush}" Name="PART_ContentBorder">
                                 <Interaction.Behaviors>
                                 <Interaction.Behaviors>

+ 1 - 5
src/PixiEditor.AvaloniaUI/Styles/Templates/TimelineSlider.axaml

@@ -44,16 +44,12 @@
                                ScaleFactor="{TemplateBinding Scale}"
                                ScaleFactor="{TemplateBinding Scale}"
                                Offset="{TemplateBinding Offset}"
                                Offset="{TemplateBinding Offset}"
                                Orientation="Horizontal">
                                Orientation="Horizontal">
-                            <Track.DecreaseButton>
-                                <RepeatButton Name="PART_DecreaseButton"
-                                              Theme="{StaticResource SliderRepeatTrackTheme}" />
-                            </Track.DecreaseButton>
                             <Track.IncreaseButton>
                             <Track.IncreaseButton>
                                 <RepeatButton Name="PART_IncreaseButton"
                                 <RepeatButton Name="PART_IncreaseButton"
                                               Theme="{StaticResource SliderRepeatTrackTheme}" />
                                               Theme="{StaticResource SliderRepeatTrackTheme}" />
                             </Track.IncreaseButton>
                             </Track.IncreaseButton>
                             <Thumb Name="thumb" VerticalAlignment="Top"
                             <Thumb Name="thumb" VerticalAlignment="Top"
-                                   MinWidth="30"
+                                   MinWidth="30" IsHitTestVisible="False"
                                    MinHeight="20">
                                    MinHeight="20">
                                 <Thumb.Template>
                                 <Thumb.Template>
                                     <ControlTemplate>
                                     <ControlTemplate>

+ 43 - 32
src/PixiEditor.AvaloniaUI/Views/Animations/Timeline.cs

@@ -1,6 +1,4 @@
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Windows.Input;
+using System.Windows.Input;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
@@ -36,11 +34,12 @@ internal class Timeline : TemplatedControl
 
 
     public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<Timeline, double>(
     public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<Timeline, double>(
         nameof(Scale), 100);
         nameof(Scale), 100);
-    
+
     public static readonly StyledProperty<int> FpsProperty = AvaloniaProperty.Register<Timeline, int>(nameof(Fps), 60);
     public static readonly StyledProperty<int> FpsProperty = AvaloniaProperty.Register<Timeline, int>(nameof(Fps), 60);
 
 
-    public static readonly StyledProperty<KeyFrameViewModel> SelectedKeyFrameProperty = AvaloniaProperty.Register<Timeline, KeyFrameViewModel>(
-        "SelectedKeyFrame");
+    public static readonly StyledProperty<KeyFrameViewModel> SelectedKeyFrameProperty =
+        AvaloniaProperty.Register<Timeline, KeyFrameViewModel>(
+            "SelectedKeyFrame");
 
 
     public static readonly StyledProperty<Vector> ScrollOffsetProperty = AvaloniaProperty.Register<Timeline, Vector>(
     public static readonly StyledProperty<Vector> ScrollOffsetProperty = AvaloniaProperty.Register<Timeline, Vector>(
         "ScrollOffset");
         "ScrollOffset");
@@ -73,8 +72,9 @@ internal class Timeline : TemplatedControl
         AvaloniaProperty.Register<Timeline, ICommand>(
         AvaloniaProperty.Register<Timeline, ICommand>(
             nameof(DuplicateKeyFrameCommand));
             nameof(DuplicateKeyFrameCommand));
 
 
-    public static readonly StyledProperty<ICommand> DeleteKeyFrameCommandProperty = AvaloniaProperty.Register<Timeline, ICommand>(
-        nameof(DeleteKeyFrameCommand));
+    public static readonly StyledProperty<ICommand> DeleteKeyFrameCommandProperty =
+        AvaloniaProperty.Register<Timeline, ICommand>(
+            nameof(DeleteKeyFrameCommand));
 
 
     public static readonly StyledProperty<double> MinLeftOffsetProperty = AvaloniaProperty.Register<Timeline, double>(
     public static readonly StyledProperty<double> MinLeftOffsetProperty = AvaloniaProperty.Register<Timeline, double>(
         nameof(MinLeftOffset), 30);
         nameof(MinLeftOffset), 30);
@@ -90,7 +90,7 @@ internal class Timeline : TemplatedControl
         get => GetValue(DeleteKeyFrameCommandProperty);
         get => GetValue(DeleteKeyFrameCommandProperty);
         set => SetValue(DeleteKeyFrameCommandProperty, value);
         set => SetValue(DeleteKeyFrameCommandProperty, value);
     }
     }
-    
+
     public ICommand DuplicateKeyFrameCommand
     public ICommand DuplicateKeyFrameCommand
     {
     {
         get => GetValue(DuplicateKeyFrameCommandProperty);
         get => GetValue(DuplicateKeyFrameCommandProperty);
@@ -139,7 +139,8 @@ internal class Timeline : TemplatedControl
 
 
     public Timeline()
     public Timeline()
     {
     {
-        _playTimer = new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000f / Fps) };
+        _playTimer =
+            new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000f / Fps) };
         _playTimer.Tick += PlayTimerOnTick;
         _playTimer.Tick += PlayTimerOnTick;
         SelectKeyFrameCommand = new RelayCommand<KeyFrameViewModel>(keyFrame =>
         SelectKeyFrameCommand = new RelayCommand<KeyFrameViewModel>(keyFrame =>
         {
         {
@@ -156,7 +157,7 @@ internal class Timeline : TemplatedControl
     {
     {
         IsPlaying = false;
         IsPlaying = false;
     }
     }
-    
+
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     {
     {
         base.OnApplyTemplate(e);
         base.OnApplyTemplate(e);
@@ -166,15 +167,15 @@ internal class Timeline : TemplatedControl
         {
         {
             _playToggle.Click += PlayToggleOnClick;
             _playToggle.Click += PlayToggleOnClick;
         }
         }
-        
+
         _contentBorder = e.NameScope.Find<Border>("PART_ContentBorder");
         _contentBorder = e.NameScope.Find<Border>("PART_ContentBorder");
-        
+
         _timelineSlider = e.NameScope.Find<TimelineSlider>("PART_TimelineSlider");
         _timelineSlider = e.NameScope.Find<TimelineSlider>("PART_TimelineSlider");
         _timelineSlider.PointerWheelChanged += TimelineSliderOnPointerWheelChanged;
         _timelineSlider.PointerWheelChanged += TimelineSliderOnPointerWheelChanged;
-        
+
         _timelineKeyFramesScroll = e.NameScope.Find<ScrollViewer>("PART_TimelineKeyFramesScroll");
         _timelineKeyFramesScroll = e.NameScope.Find<ScrollViewer>("PART_TimelineKeyFramesScroll");
         _timelineHeaderScroll = e.NameScope.Find<ScrollViewer>("PART_TimelineHeaderScroll");
         _timelineHeaderScroll = e.NameScope.Find<ScrollViewer>("PART_TimelineHeaderScroll");
-        
+
         _timelineKeyFramesScroll.ScrollChanged += TimelineKeyFramesScrollOnScrollChanged;
         _timelineKeyFramesScroll.ScrollChanged += TimelineKeyFramesScrollOnScrollChanged;
     }
     }
 
 
@@ -218,13 +219,13 @@ internal class Timeline : TemplatedControl
             IsPlaying = false;
             IsPlaying = false;
         }
         }
     }
     }
-    
+
     private void TimelineSliderOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
     private void TimelineSliderOnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
     {
     {
         double newScale = Scale;
         double newScale = Scale;
-        
+
         int ticks = e.KeyModifiers.HasFlag(KeyModifiers.Control) ? 1 : 10;
         int ticks = e.KeyModifiers.HasFlag(KeyModifiers.Control) ? 1 : 10;
-        
+
         int towardsFrame = MousePosToFrame(e);
         int towardsFrame = MousePosToFrame(e);
 
 
         if (e.Delta.Y > 0)
         if (e.Delta.Y > 0)
@@ -238,15 +239,25 @@ internal class Timeline : TemplatedControl
         
         
         newScale = Math.Clamp(newScale, 1, 900);
         newScale = Math.Clamp(newScale, 1, 900);
         Scale = newScale;
         Scale = newScale;
-        
-        Dispatcher.UIThread.Post(() =>
+
+        Dispatcher.UIThread.Post(
+            () =>
         {
         {
-            //ScrollOffset = new Vector(towardsFrame * Scale, 0);
-        });
-        
+            double mouseXInViewport = e.GetPosition(_timelineKeyFramesScroll).X;
+            
+            double currentFrameUnderMouse = towardsFrame;
+            double newOffsetX = currentFrameUnderMouse * newScale - mouseXInViewport + MinLeftOffset; // Where the new scroll position should be
+
+            // Clamp the new offset within the scrollable range
+            newOffsetX = Math.Clamp(newOffsetX, 0, _timelineKeyFramesScroll.ScrollBarMaximum.X);
+
+            // Update the scroll offset
+            ScrollOffset = new Vector(newOffsetX, 0);
+        }, DispatcherPriority.Render);
+
         e.Handled = true;
         e.Handled = true;
     }
     }
-    
+
     private int MousePosToFrame(PointerEventArgs e, bool round = true)
     private int MousePosToFrame(PointerEventArgs e, bool round = true)
     {
     {
         double x = e.GetPosition(_contentBorder).X;
         double x = e.GetPosition(_contentBorder).X;
@@ -260,7 +271,7 @@ internal class Timeline : TemplatedControl
         {
         {
             frame = (int)Math.Floor(x / Scale);
             frame = (int)Math.Floor(x / Scale);
         }
         }
-        
+
         frame = Math.Max(0, frame);
         frame = Math.Max(0, frame);
         return frame;
         return frame;
     }
     }
@@ -281,7 +292,7 @@ internal class Timeline : TemplatedControl
             timeline._playTimer.Stop();
             timeline._playTimer.Stop();
         }
         }
     }
     }
-    
+
     private static void FpsChanged(AvaloniaPropertyChangedEventArgs e)
     private static void FpsChanged(AvaloniaPropertyChangedEventArgs e)
     {
     {
         if (e.Sender is not Timeline timeline)
         if (e.Sender is not Timeline timeline)
@@ -291,7 +302,7 @@ internal class Timeline : TemplatedControl
 
 
         timeline._playTimer.Interval = TimeSpan.FromMilliseconds(1000f / timeline.Fps);
         timeline._playTimer.Interval = TimeSpan.FromMilliseconds(1000f / timeline.Fps);
     }
     }
-    
+
     private static void OnKeyFramesChanged(AvaloniaPropertyChangedEventArgs e)
     private static void OnKeyFramesChanged(AvaloniaPropertyChangedEventArgs e)
     {
     {
         if (e.Sender is not Timeline timeline)
         if (e.Sender is not Timeline timeline)
@@ -299,24 +310,24 @@ internal class Timeline : TemplatedControl
             return;
             return;
         }
         }
 
 
-        if(e.OldValue is KeyFrameCollection oldCollection)
+        if (e.OldValue is KeyFrameCollection oldCollection)
         {
         {
             oldCollection.KeyFrameAdded -= timeline.KeyFrames_KeyFrameAdded;
             oldCollection.KeyFrameAdded -= timeline.KeyFrames_KeyFrameAdded;
             oldCollection.KeyFrameRemoved -= timeline.KeyFrames_KeyFrameRemoved;
             oldCollection.KeyFrameRemoved -= timeline.KeyFrames_KeyFrameRemoved;
         }
         }
-        
-        if(e.NewValue is KeyFrameCollection newCollection)
+
+        if (e.NewValue is KeyFrameCollection newCollection)
         {
         {
             newCollection.KeyFrameAdded += timeline.KeyFrames_KeyFrameAdded;
             newCollection.KeyFrameAdded += timeline.KeyFrames_KeyFrameAdded;
             newCollection.KeyFrameRemoved += timeline.KeyFrames_KeyFrameRemoved;
             newCollection.KeyFrameRemoved += timeline.KeyFrames_KeyFrameRemoved;
         }
         }
     }
     }
-    
+
     private void KeyFrames_KeyFrameAdded(KeyFrameViewModel keyFrame)
     private void KeyFrames_KeyFrameAdded(KeyFrameViewModel keyFrame)
     {
     {
         SelectedKeyFrame = keyFrame;
         SelectedKeyFrame = keyFrame;
     }
     }
-    
+
     private void KeyFrames_KeyFrameRemoved(KeyFrameViewModel keyFrame)
     private void KeyFrames_KeyFrameRemoved(KeyFrameViewModel keyFrame)
     {
     {
         if (SelectedKeyFrame == keyFrame)
         if (SelectedKeyFrame == keyFrame)

+ 0 - 12
src/PixiEditor.AvaloniaUI/Views/Animations/TimelineSlider.cs

@@ -38,12 +38,9 @@ public class TimelineSlider : Slider
     }
     }
 
 
     private Button _increaseButton;
     private Button _increaseButton;
-    private Button _decreaseButton;
     private Track _track;
     private Track _track;
     
     
     private bool _isDragging;
     private bool _isDragging;
-    private IDisposable? _decreaseButtonPressDispose;
-    private IDisposable? _decreaseButtonReleaseDispose;
     private IDisposable? _increaseButtonSubscription;
     private IDisposable? _increaseButtonSubscription;
     private IDisposable? _increaseButtonReleaseDispose;
     private IDisposable? _increaseButtonReleaseDispose;
     private IDisposable? _pointerMovedDispose;
     private IDisposable? _pointerMovedDispose;
@@ -55,14 +52,11 @@ public class TimelineSlider : Slider
 
 
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     {
     {
-        _decreaseButtonPressDispose?.Dispose();
-        _decreaseButtonReleaseDispose?.Dispose();
         _increaseButtonSubscription?.Dispose();
         _increaseButtonSubscription?.Dispose();
         _increaseButtonReleaseDispose?.Dispose();
         _increaseButtonReleaseDispose?.Dispose();
         _pointerMovedDispose?.Dispose();
         _pointerMovedDispose?.Dispose();
         
         
         _increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
         _increaseButton = e.NameScope.Find<Button>("PART_IncreaseButton");
-        _decreaseButton = e.NameScope.Find<Button>("PART_DecreaseButton");
         _track = e.NameScope.Find<Track>("PART_Track");
         _track = e.NameScope.Find<Track>("PART_Track");
         
         
         if (_track != null)
         if (_track != null)
@@ -70,12 +64,6 @@ public class TimelineSlider : Slider
             _track.IgnoreThumbDrag = true;
             _track.IgnoreThumbDrag = true;
         }
         }
 
 
-        if (_decreaseButton != null)
-        {
-            _decreaseButtonPressDispose = _decreaseButton.AddDisposableHandler(PointerPressedEvent, TrackPressed, RoutingStrategies.Tunnel);
-            _decreaseButtonReleaseDispose = _decreaseButton.AddDisposableHandler(PointerReleasedEvent, TrackReleased, RoutingStrategies.Tunnel);
-        }
-
         if (_increaseButton != null)
         if (_increaseButton != null)
         {
         {
             _increaseButtonSubscription = _increaseButton.AddDisposableHandler(PointerPressedEvent, TrackPressed, RoutingStrategies.Tunnel);
             _increaseButtonSubscription = _increaseButton.AddDisposableHandler(PointerPressedEvent, TrackPressed, RoutingStrategies.Tunnel);

+ 1 - 15
src/PixiEditor.AvaloniaUI/Views/Animations/TimelineSliderTrack.cs

@@ -44,21 +44,7 @@ internal class TimelineSliderTrack : Track
             double thumbPosition = scaledValue - Offset.X;
             double thumbPosition = scaledValue - Offset.X;
 
 
             Thumb.Arrange(new Rect(thumbPosition, 0, thumbLength, finalSize.Height));
             Thumb.Arrange(new Rect(thumbPosition, 0, thumbLength, finalSize.Height));
-            
-            Rect decreaseButtonRect = new Rect(0, 0, thumbPosition, finalSize.Height);
-            Rect increaseButtonRect = new Rect(thumbPosition + thumbLength, 0, finalSize.Width - thumbPosition - thumbLength, finalSize.Height);
-            if (decreaseButtonRect.Width < 0)
-            {
-                decreaseButtonRect = new Rect(0, 0, 0, 0);
-            }
-            
-            if (increaseButtonRect.Width < 0)
-            {
-                increaseButtonRect = new Rect(0, 0, 0, 0);
-            }
-            
-            DecreaseButton?.Arrange(decreaseButtonRect);
-            IncreaseButton?.Arrange(increaseButtonRect);
+            IncreaseButton?.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
         }
         }
 
 
         return finalSize;
         return finalSize;