Browse Source

added step buttons in animation

Krzysztof Krysiński 2 months ago
parent
commit
e3bb33ef79

+ 5 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1055,5 +1055,9 @@
   "EXTRACT_SELECTED_TEXT": "Extract selected text",
   "EXTRACT_SELECTED_TEXT_DESCRIPTIVE": "Extract selected text into new layer.",
   "EXTRACT_SELECTED_CHARACTERS": "Extract selected characters",
-  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Extract individual characters from selection into new layers."
+  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Extract individual characters from selection into new layers.",
+  "STEP_START": "Step back to closest cel",
+  "STEP_END": "Step forward to closest cel",
+  "STEP_FORWARD": "Step forward one frame",
+  "STEP_BACK": "Step back one frame"
 }

+ 2 - 0
src/PixiEditor/Styles/PortingWipStyles.axaml

@@ -50,6 +50,7 @@
     <Style Selector="ToggleButton.PlayButton">
         <Setter Property="Content" Value="{DynamicResource icon-play}" />
         <Setter Property="Background" Value="Transparent"/>
+        <Setter Property="Foreground" Value="{DynamicResource ThemeAccent2Brush}"/>
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate>
@@ -64,6 +65,7 @@
 
     <Style Selector="ToggleButton.PlayButton:checked">
         <Setter Property="Content" Value="{DynamicResource icon-pause}" />
+        <Setter Property="Foreground" Value="{DynamicResource ThemeAccentHighBrush}"/>
         <Setter Property="Background" Value="Transparent" />
     </Style>
 

+ 15 - 4
src/PixiEditor/Styles/Templates/Timeline.axaml

@@ -77,13 +77,24 @@
                     </Border>
                     <Border Grid.Row="0" Grid.Column="1">
                         <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="5">
-                            <Button />
-                            <Button />
+                            <Button Classes="pixi-icon" Content="{DynamicResource icon-step-start}"
+                                    ui1:Translator.TooltipKey="STEP_START"
+                                    Command="{Binding StepStartCommand, RelativeSource={RelativeSource TemplatedParent}}"/>
+                            <Button Classes="pixi-icon"
+                                    ui1:Translator.TooltipKey="STEP_BACK"
+                                    Command="{Binding StepBackCommand, RelativeSource={RelativeSource TemplatedParent}}"
+                                    Content="{DynamicResource icon-step-back}" />
                             <ToggleButton Margin="0, 5" Width="24" HorizontalAlignment="Center" Classes="PlayButton"
                                           Name="PART_PlayToggle"
                                           IsChecked="{Binding IsPlaying, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
-                            <Button/>
-                            <Button/>
+                            <Button Classes="pixi-icon"
+                                    ui1:Translator.TooltipKey="STEP_FORWARD"
+                                    Command="{Binding StepForwardCommand, RelativeSource={RelativeSource TemplatedParent}}"
+                                    Content="{DynamicResource icon-step-forward}" />
+                            <Button Classes="pixi-icon"
+                                    ui1:Translator.TooltipKey="STEP_END"
+                                    Command="{Binding StepEndCommand, RelativeSource={RelativeSource TemplatedParent}}"
+                                    Content="{DynamicResource icon-step-end}" />
                             <TextBlock VerticalAlignment="Center" FontSize="14">
                                 <Run>
                                     <Run.Text>

+ 5 - 1
src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs

@@ -34,12 +34,14 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
     private int? cachedFirstFrame;
     private int? cachedLastFrame;
 
+    private bool blockUpdateFrame = false;
+
     public int ActiveFrameBindable
     {
         get => _activeFrameBindable;
         set
         {
-            if (Document.BlockingUpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive || blockUpdateFrame)
                 return;
 
             Internals.ActionAccumulator.AddActions(new SetActiveFrame_PassthroughAction(value));
@@ -201,7 +203,9 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         int previousFrame = _activeFrameBindable;
         _activeFrameBindable = newFrame;
         ActiveFrameChanged?.Invoke(previousFrame, newFrame);
+        blockUpdateFrame = true;
         OnPropertyChanged(nameof(ActiveFrameBindable));
+        blockUpdateFrame = false;
     }
 
     public void SetPlayingState(bool value)

+ 54 - 0
src/PixiEditor/Views/Animations/Timeline.cs

@@ -176,6 +176,11 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
     public ICommand ClearSelectedKeyFramesCommand { get; }
     public ICommand PressedKeyFrameCommand { get; }
 
+    public ICommand StepStartCommand { get; }
+    public ICommand StepEndCommand { get; }
+    public ICommand StepForwardCommand { get; }
+    public ICommand StepBackCommand { get; }
+
     public IReadOnlyCollection<CelViewModel> SelectedKeyFrames => KeyFrames != null
         ? KeyFrames.SelectChildrenBy<CelViewModel>(x => x.IsSelected).ToList()
         : [];
@@ -212,6 +217,55 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         ClearSelectedKeyFramesCommand = new RelayCommand<CelViewModel>(ClearSelectedKeyFrames);
         DraggedKeyFrameCommand = new RelayCommand<PointerEventArgs>(KeyFramesDragged);
         ReleasedKeyFrameCommand = new RelayCommand<CelViewModel>(KeyFramesReleased);
+
+        StepStartCommand = new RelayCommand(() =>
+        {
+            var keyFramesWithinActiveFrame = KeyFrames.Where(x => x.IsVisible
+                                                                  && x.StartFrameBindable < ActiveFrame).SelectMany(x => x.Children).ToList();
+            if (keyFramesWithinActiveFrame.Count > 0)
+            {
+                List<int> snapPoints = keyFramesWithinActiveFrame.Select(x => x.StartFrameBindable + x.DurationBindable - 1).ToList();
+                snapPoints.AddRange(KeyFrames.Select(x => x.StartFrameBindable));
+                snapPoints.RemoveAll(x => x >= ActiveFrame);
+
+                ActiveFrame = snapPoints.Max();
+            }
+            else
+            {
+                ActiveFrame = 1;
+            }
+        });
+
+        StepEndCommand = new RelayCommand(() =>
+        {
+            var keyFramesWithinActiveFrame = KeyFrames.Where(x => x.IsVisible
+                                                                  && x.StartFrameBindable + x.DurationBindable - 1 > ActiveFrame).SelectMany(x => x.Children).ToList();
+            if (keyFramesWithinActiveFrame.Count > 0)
+            {
+                List<int> snapPoints = keyFramesWithinActiveFrame.Select(x => x.StartFrameBindable + x.DurationBindable - 1).ToList();
+                snapPoints.AddRange(KeyFrames.Select(x => x.StartFrameBindable));
+                snapPoints.RemoveAll(x => x <= ActiveFrame);
+
+                ActiveFrame = snapPoints.Min();
+            }
+            else
+            {
+                ActiveFrame = EndFrame;
+            }
+        });
+
+        StepForwardCommand = new RelayCommand(() =>
+        {
+            ActiveFrame++;
+        });
+
+        StepBackCommand = new RelayCommand(() =>
+        {
+            if (ActiveFrame > 1)
+            {
+                ActiveFrame--;
+            }
+        });
     }
 
     public void SelectKeyFrame(ICelHandler? keyFrame, bool clearSelection = true)