flabbet 1 year ago
parent
commit
4b1301fc92
20 changed files with 164 additions and 58 deletions
  1. 2 5
      src/PixiEditor.AvaloniaUI/Models/Handlers/IAnimationHandler.cs
  2. 9 0
      src/PixiEditor.AvaloniaUI/Models/Handlers/IKeyFrameGroupHandler.cs
  3. 4 1
      src/PixiEditor.AvaloniaUI/Models/Handlers/IKeyFrameHandler.cs
  4. 42 0
      src/PixiEditor.AvaloniaUI/Models/Rendering/MemberPreviewUpdater.cs
  5. 1 0
      src/PixiEditor.AvaloniaUI/Styles/PixiEditor.Controls.axaml
  6. 4 9
      src/PixiEditor.AvaloniaUI/Styles/Templates/KeyFrame.axaml
  7. 42 11
      src/PixiEditor.AvaloniaUI/Styles/Templates/Timeline.axaml
  8. 15 8
      src/PixiEditor.AvaloniaUI/ViewModels/Document/AnimationDataViewModel.cs
  9. 1 0
      src/PixiEditor.AvaloniaUI/ViewModels/Document/KeyFrameCollection.cs
  10. 4 4
      src/PixiEditor.AvaloniaUI/ViewModels/Document/KeyFrameGroupViewModel.cs
  11. 10 1
      src/PixiEditor.AvaloniaUI/ViewModels/Document/KeyFrameViewModel.cs
  12. 1 1
      src/PixiEditor.AvaloniaUI/ViewModels/Document/RasterKeyFrameViewModel.cs
  13. 2 2
      src/PixiEditor.AvaloniaUI/ViewModels/Document/StructureMemberViewModel.cs
  14. 3 1
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/AnimationsViewModel.cs
  15. 10 1
      src/PixiEditor.AvaloniaUI/Views/Animations/KeyFrame.cs
  16. 1 1
      src/PixiEditor.AvaloniaUI/Views/Animations/Timeline.cs
  17. 9 8
      src/PixiEditor.ChangeableDocument/Changeables/Animations/AnimationData.cs
  18. 1 2
      src/PixiEditor.ChangeableDocument/Changeables/Animations/RasterKeyFrame.cs
  19. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyAnimationData.cs
  20. 2 3
      src/PixiEditor.ChangeableDocument/Changes/Animation/CreateRasterKeyFrame_Change.cs

+ 2 - 5
src/PixiEditor.AvaloniaUI/Models/Handlers/IAnimationHandler.cs

@@ -1,8 +1,4 @@
-using System.Collections.ObjectModel;
-using PixiEditor.AvaloniaUI.ViewModels.Document;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-
-namespace PixiEditor.AvaloniaUI.Models.Handlers;
+namespace PixiEditor.AvaloniaUI.Models.Handlers;
 
 public interface IAnimationHandler
 {
@@ -10,6 +6,7 @@ public interface IAnimationHandler
     public int ActiveFrameBindable { get; set; }
     public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, bool cloneFromExisting);
     public void SetActiveFrame(int newFrame);
+    public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : IKeyFrameHandler;
     internal void AddKeyFrame(IKeyFrameHandler keyFrame);
     internal void RemoveKeyFrame(Guid keyFrameId);
 }

+ 9 - 0
src/PixiEditor.AvaloniaUI/Models/Handlers/IKeyFrameGroupHandler.cs

@@ -0,0 +1,9 @@
+using System.Collections.ObjectModel;
+using ChunkyImageLib;
+
+namespace PixiEditor.AvaloniaUI.Models.Handlers;
+
+public interface IKeyFrameGroupHandler : IKeyFrameHandler
+{
+    public ObservableCollection<IKeyFrameHandler> Children { get; }
+}

+ 4 - 1
src/PixiEditor.AvaloniaUI/Models/Handlers/IKeyFrameHandler.cs

@@ -1,7 +1,10 @@
-namespace PixiEditor.AvaloniaUI.Models.Handlers;
+using ChunkyImageLib;
+
+namespace PixiEditor.AvaloniaUI.Models.Handlers;
 
 public interface IKeyFrameHandler
 {
+    public Surface? Preview { get; set; }
     public int StartFrame { get; }
     public int Duration { get; }
     public Guid LayerGuid { get; }

+ 42 - 0
src/PixiEditor.AvaloniaUI/Models/Rendering/MemberPreviewUpdater.cs

@@ -10,6 +10,7 @@ using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.DocumentModels;
 using PixiEditor.AvaloniaUI.Models.Handlers;
 using PixiEditor.AvaloniaUI.Models.Rendering.RenderInfos;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.Numerics;
@@ -209,21 +210,36 @@ internal class MemberPreviewUpdater
         {
             IStructureMemberHandler member = doc.StructureHelper.FindOrThrow(guid);
 
+            bool hasAnimation = doc.AnimationHandler.FindKeyFrame<IKeyFrameGroupHandler>(guid, out IKeyFrameGroupHandler? animationGroup);
+            IKeyFrameHandler? keyFrame = hasAnimation ? animationGroup!.Children.ElementAtOrDefault(doc.AnimationHandler.ActiveFrameBindable) : null;
+
             if (newSize is null)
             {
                 member.PreviewSurface?.Dispose();
                 member.PreviewSurface = null;
+
+                keyFrame?.Preview?.Dispose();
+                if (keyFrame != null)
+                {
+                    keyFrame.Preview = null;
+                }
             }
             else
             {
                 if (member.PreviewSurface is not null && member.PreviewSurface.Size.X == newSize.Value.previewSize.X && member.PreviewSurface.Size.Y == newSize.Value.previewSize.Y)
                 {
                     member.PreviewSurface!.DrawingSurface.Canvas.Clear();
+                    keyFrame?.Preview?.DrawingSurface.Canvas.Clear();
                 }
                 else
                 {
                     member.PreviewSurface?.Dispose();
                     member.PreviewSurface = new Surface(newSize.Value.previewSize); // TODO: premul bgra8888 was here
+                    keyFrame?.Preview?.Dispose();
+                    if (keyFrame != null)
+                    {
+                        keyFrame.Preview = new Surface(newSize.Value.previewSize);
+                    }
                 }
             }
 
@@ -474,6 +490,19 @@ internal class MemberPreviewUpdater
             if (memberVM is ILayerHandler)
             {
                 RenderLayerMainPreview((IReadOnlyLayer)member, memberVM, affArea.Value, position, scaling);
+
+                if (doc.AnimationHandler.FindKeyFrame(guid, out IKeyFrameHandler? keyFrame))
+                {
+                    if (keyFrame is IKeyFrameGroupHandler group)
+                    {
+                        if (group.Children.Count > doc.AnimationHandler.ActiveFrameBindable)
+                        {
+                            var target = group.Children[doc.AnimationHandler.ActiveFrameBindable];
+                            RenderAnimationFramePreview(memberVM, target);
+                        }
+                    }
+                }
+                
                 infos.Add(new PreviewDirty_RenderInfo(guid));
             }
             else if (memberVM is IFolderHandler)
@@ -535,6 +564,19 @@ internal class MemberPreviewUpdater
 
         memberVM.PreviewSurface.DrawingSurface.Canvas.Restore();
     }
+    
+    private void RenderAnimationFramePreview(IStructureMemberHandler memberVM, IKeyFrameHandler keyFrame)
+    {
+        if (keyFrame.Preview is null)
+        {
+            if(memberVM.PreviewSurface is not null)
+                keyFrame.Preview = new Surface(memberVM.PreviewSurface.Size);
+            else
+                return;
+        }
+
+        keyFrame.Preview.DrawingSurface.Canvas.DrawSurface(memberVM.PreviewSurface.DrawingSurface, VecI.Zero, ReplacingPaint);
+    }
 
     private void RenderMaskPreviews(
         Dictionary<Guid, AffectedArea> maskPreviewChunks,

+ 1 - 0
src/PixiEditor.AvaloniaUI/Styles/PixiEditor.Controls.axaml

@@ -10,6 +10,7 @@
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/ShortcutBoxTemplate.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/DocumentTabTemplate.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/Timeline.axaml"/>
+                <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/KeyFrame.axaml"/>
             </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>
     </Styles.Resources>

+ 4 - 9
src/PixiEditor.AvaloniaUI/Styles/Templates/KeyFrame.axaml

@@ -1,18 +1,13 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:animations="clr-namespace:PixiEditor.AvaloniaUI.Views.Animations"
-                    xmlns:document="clr-namespace:PixiEditor.AvaloniaUI.ViewModels.Document"
-                    xmlns:handlers="clr-namespace:PixiEditor.AvaloniaUI.Models.Handlers"
-                    xmlns:behaviours="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Behaviours"
-                    xmlns:commands="clr-namespace:PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands"
-                    xmlns:xaml="clr-namespace:PixiEditor.AvaloniaUI.Models.Commands.XAML">
-    <Design.PreviewWith>
-    </Design.PreviewWith>
-
+                    xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals">
     <ControlTheme TargetType="animations:KeyFrame" x:Key="{x:Type animations:KeyFrame}">
         <Setter Property="Template">
             <ControlTemplate>
-
+                <Grid>
+                    <visuals:SurfaceControl Surface="{Binding Item.Preview, RelativeSource={RelativeSource TemplatedParent}}"/>
+                </Grid>
             </ControlTemplate>
         </Setter>
     </ControlTheme>

+ 42 - 11
src/PixiEditor.AvaloniaUI/Styles/Templates/Timeline.axaml

@@ -10,7 +10,7 @@
     <Design.PreviewWith>
         <animations:Timeline>
             <animations:Timeline.KeyFrames>
-                <document:RasterKeyFrameViewModel Duration="100"/>
+                <document:RasterKeyFrameViewModel Duration="100" />
             </animations:Timeline.KeyFrames>
         </animations:Timeline>
     </Design.PreviewWith>
@@ -20,26 +20,26 @@
             <ControlTemplate>
                 <Grid>
                     <Grid.RowDefinitions>
-                        <RowDefinition Height="Auto"/>
-                        <RowDefinition Height="Auto"/>
-                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="Auto" />
+                        <RowDefinition Height="*" />
                     </Grid.RowDefinitions>
 
                     <DockPanel Grid.Row="0" LastChildFill="True">
                         <Button DockPanel.Dock="Left" Classes="pixi-icon"
                                 Content="{DynamicResource icon-plus-square}"
-                                Command="{TemplateBinding NewKeyFrameCommand}"/>
-                        
+                                Command="{TemplateBinding NewKeyFrameCommand}" />
+
                         <Button DockPanel.Dock="Left" Classes="pixi-icon"
                                 Content="{DynamicResource icon-duplicate}"
-                                Command="{TemplateBinding DuplicateKeyFrameCommand}"/>
+                                Command="{TemplateBinding DuplicateKeyFrameCommand}" />
                         <Panel>
-                            <ToggleButton HorizontalAlignment="Center" Content="Play" Name="PART_PlayToggle"/>
+                            <ToggleButton HorizontalAlignment="Center" Content="Play" Name="PART_PlayToggle" />
                         </Panel>
                     </DockPanel>
 
                     <DockPanel Grid.Row="1" LastChildFill="True">
-                        <Slider Margin="10 0" TickFrequency="1" TickPlacement="BottomRight" 
+                        <Slider Margin="10 0" TickFrequency="1" TickPlacement="BottomRight"
                                 SmallChange="1"
                                 LargeChange="10"
                                 IsSnapToTickEnabled="True"
@@ -57,14 +57,45 @@
                             </Interaction.Behaviors>
                         </Slider>
                     </DockPanel>
-                    
+
                     <ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Auto">
                         <TreeView ItemsSource="{TemplateBinding KeyFrames}">
+                            <TreeView.ItemContainerTheme>
+                                <ControlTheme TargetType="TreeViewItem">
+                                    <Setter Property="Template">
+                                        <ControlTemplate>
+                                            <StackPanel>
+                                                <ItemsPresenter Name="PART_ItemsPresenter"
+                                                                IsVisible="True">
+                                                    <ItemsPresenter.ItemsPanel>
+                                                        <ItemsPanelTemplate>
+                                                            <StackPanel Orientation="Horizontal" />
+                                                        </ItemsPanelTemplate>
+                                                    </ItemsPresenter.ItemsPanel>
+                                                </ItemsPresenter>
+                                                <ContentPresenter Name="PART_HeaderPresenter"
+                                                                  Grid.Column="1"
+                                                                  Background="Transparent"
+                                                                  Padding="{TemplateBinding Padding}"
+                                                                  HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
+                                                                  Content="{TemplateBinding Header}"
+                                                                  ContentTemplate="{TemplateBinding HeaderTemplate}"
+                                                                  Focusable="False" />
+                                            </StackPanel>
+                                        </ControlTemplate>
+                                    </Setter>
+                                </ControlTheme>
+                            </TreeView.ItemContainerTheme>
                             <TreeView.ItemTemplate>
                                 <TreeDataTemplate ItemsSource="{Binding Children}">
-                                    <TextBlock Text="{Binding StartFrame}"/>
+                                    <animations:KeyFrame Item="{Binding}" />
                                 </TreeDataTemplate>
                             </TreeView.ItemTemplate>
+                            <TreeView.ItemsPanel>
+                                <ItemsPanelTemplate>
+                                    <StackPanel Orientation="Horizontal" />
+                                </ItemsPanelTemplate>
+                            </TreeView.ItemsPanel>
                         </TreeView>
                     </ScrollViewer>
                 </Grid>

+ 15 - 8
src/PixiEditor.AvaloniaUI/ViewModels/Document/AnimationDataViewModel.cs

@@ -39,8 +39,10 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
     public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, bool cloneFromExisting)
     {
         if (!Document.UpdateableChangeActive)
-            Internals.ActionAccumulator.AddFinishedActions(new CreateRasterClip_Action(targetLayerGuid, frame,
+        {
+            Internals.ActionAccumulator.AddFinishedActions(new CreateRasterKeyFrame_Action(targetLayerGuid, frame,
                 cloneFromExisting));
+        }
     }
 
     public void SetActiveFrame(int newFrame)
@@ -74,20 +76,25 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
             parent.Children.Remove(frame);
         });
     }
+    
+    public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : IKeyFrameHandler
+    {
+        return TryFindKeyFrame<T>(keyFrames, null, guid, out keyFrameHandler, null);
+    }
 
     // TODO: Use the same structure functions as layers
-    public bool TryFindKeyFrame<T>(Guid id, out T foundKeyFrame,
-        Action<KeyFrameViewModel, KeyFrameGroupViewModel?> onFound = null) where T : KeyFrameViewModel
+    public bool TryFindKeyFrame<T>(Guid id, out T? foundKeyFrame,
+        Action<IKeyFrameHandler, IKeyFrameGroupHandler?> onFound = null) where T : IKeyFrameHandler
     {
         return TryFindKeyFrame(keyFrames, null, id, out foundKeyFrame, onFound);
     }
 
-    private bool TryFindKeyFrame<T>(IList<KeyFrameViewModel> root, KeyFrameGroupViewModel parent, Guid id, out T result,
-        Action<KeyFrameViewModel, KeyFrameGroupViewModel?> onFound) where T : KeyFrameViewModel
+    private bool TryFindKeyFrame<T>(IReadOnlyCollection<IKeyFrameHandler> root, IKeyFrameGroupHandler parent, Guid id, out T? result,
+        Action<IKeyFrameHandler, IKeyFrameGroupHandler?> onFound) where T : IKeyFrameHandler
     {
         for (var i = 0; i < root.Count; i++)
         {
-            var frame = root[i];
+            var frame = root.ElementAt(i);
             if (frame is T targetFrame && targetFrame.Id.Equals(id))
             {
                 result = targetFrame;
@@ -95,7 +102,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
                 return true;
             }
 
-            if (frame is KeyFrameGroupViewModel { Children.Count: > 0 } group)
+            if (frame is IKeyFrameGroupHandler { Children.Count: > 0 } group)
             {
                 bool found = TryFindKeyFrame(group.Children, group, id, out result, onFound);
                 if (found)
@@ -105,7 +112,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
             }
         }
 
-        result = null;
+        result = default;
         return false;
     }
 }

+ 1 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Document/KeyFrameCollection.cs

@@ -1,6 +1,7 @@
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.ComponentModel;
+using PixiEditor.AvaloniaUI.Models.Handlers;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Document;
 

+ 4 - 4
src/PixiEditor.AvaloniaUI/ViewModels/Document/KeyFrameGroupViewModel.cs

@@ -3,12 +3,12 @@ using PixiEditor.AvaloniaUI.Models.Handlers;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Document;
 
-public class KeyFrameGroupViewModel : KeyFrameViewModel
+public class KeyFrameGroupViewModel : KeyFrameViewModel, IKeyFrameGroupHandler
 {
-    public ObservableCollection<KeyFrameViewModel> Children { get; } = new ObservableCollection<KeyFrameViewModel>();
+    public ObservableCollection<IKeyFrameHandler> Children { get; } = new ObservableCollection<IKeyFrameHandler>();
 
-    public override int StartFrame => Children.Min(x => x.StartFrame);
-    public override int Duration => Children.Max(x => x.StartFrame + x.Duration);
+    public override int StartFrame => Children.Count > 0 ? Children.Min(x => x.StartFrame) : 0;
+    public override int Duration => Children.Count > 0 ? Children.Max(x => x.StartFrame + x.Duration) : 0;
 
     public KeyFrameGroupViewModel(int startFrame, int duration, Guid layerGuid, Guid id) : base(startFrame, duration, layerGuid, id)
     {

+ 10 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Document/KeyFrameViewModel.cs

@@ -1,10 +1,19 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using ChunkyImageLib;
+using CommunityToolkit.Mvvm.ComponentModel;
 using PixiEditor.AvaloniaUI.Models.Handlers;
 
 namespace PixiEditor.AvaloniaUI.ViewModels.Document;
 
 public abstract class KeyFrameViewModel(int startFrame, int duration, Guid layerGuid, Guid id) : ObservableObject, IKeyFrameHandler
 {
+    private Surface? preview;
+    
+    public Surface? Preview
+    {
+        get => preview;
+        set => SetProperty(ref preview, value);
+    }
+    
     public virtual int StartFrame { get; } = startFrame;
     public virtual int Duration { get; } = duration;
     public Guid LayerGuid { get; } = layerGuid;

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Document/RasterKeyFrameViewModel.cs

@@ -6,7 +6,7 @@ public class RasterKeyFrameViewModel : KeyFrameViewModel, IRasterKeyFrameHandler
 {
     public RasterKeyFrameViewModel(Guid targetLayerGuid, int startFrame, int duration, Guid id) : base(startFrame, duration, targetLayerGuid, id)
     {
-
+        
     }
 
 }

+ 2 - 2
src/PixiEditor.AvaloniaUI/ViewModels/Document/StructureMemberViewModel.cs

@@ -120,9 +120,9 @@ internal abstract class StructureMemberViewModel : ObservableObject, IStructureM
 
     private float opacity;
 
-    public void SetOpacity(float opacity)
+    public void SetOpacity(float newOpacity)
     {
-        this.opacity = opacity;
+        this.opacity = newOpacity;
         OnPropertyChanged(nameof(OpacityBindable));
     }
     public float OpacityBindable

+ 3 - 1
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/AnimationsViewModel.cs

@@ -13,7 +13,7 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
     
     [Command.Basic("PixiEditor.Animation.CreateRasterKeyFrame", "Create Raster Key Frame", "Create a raster key frame", Parameter = false)]
     [Command.Basic("PixiEditor.Animation.DuplicateRasterKeyFrame", "Duplicate Raster Key Frame", "Duplicate a raster key frame", Parameter = true)]
-    public void CreateRasterClip(bool duplicate)
+    public void CreateRasterKeyFrame(bool duplicate)
     {
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (activeDocument == null)
@@ -32,6 +32,8 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
             activeDocument.SelectedStructureMember.GuidValue, 
             newFrame,
             duplicate);
+        
+        activeDocument.Operations.SetActiveFrame(newFrame);
     }
     
     [Command.Internal("PixiEditor.Document.StartChangeActiveFrame", CanExecute = "PixiEditor.HasDocument")]

+ 10 - 1
src/PixiEditor.AvaloniaUI/Views/Animations/KeyFrame.cs

@@ -1,8 +1,17 @@
-using Avalonia.Controls.Primitives;
+using Avalonia;
+using Avalonia.Controls.Primitives;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
 
 namespace PixiEditor.AvaloniaUI.Views.Animations;
 
 public class KeyFrame : TemplatedControl
 {
+    public static readonly StyledProperty<KeyFrameViewModel> ItemProperty = AvaloniaProperty.Register<KeyFrame, KeyFrameViewModel>(
+        nameof(Item));
 
+    public KeyFrameViewModel Item
+    {
+        get => GetValue(ItemProperty);
+        set => SetValue(ItemProperty, value);
+    }
 }

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Animations/Timeline.cs

@@ -70,7 +70,7 @@ public class Timeline : TemplatedControl
 
     public Timeline()
     {
-        _playTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1000 / 8f) };
+        _playTimer = new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000 / 8f) };
         _playTimer.Tick += PlayTimerOnTick;
     }
 

+ 9 - 8
src/PixiEditor.ChangeableDocument/Changeables/Animations/AnimationData.cs

@@ -29,11 +29,6 @@ public class AnimationData : IReadOnlyAnimationData
 
     private List<KeyFrame> keyFrames = new List<KeyFrame>();
     
-    public void ChangePreviewFrame(int frame)
-    {
-        ActiveFrame = frame;
-    }
-
     public void AddKeyFrame(KeyFrame keyFrame)
     {
         Guid id = keyFrame.LayerGuid;
@@ -59,13 +54,18 @@ public class AnimationData : IReadOnlyAnimationData
             }
         });
     }
+    
+    public bool FindKeyFrame(Guid id, out IReadOnlyKeyFrame keyFrame)
+    {
+        return TryFindKeyFrame(id, out keyFrame, null);
+    }
 
-    private bool TryFindKeyFrame<T>(Guid id, out T foundKeyFrame, Action<KeyFrame, GroupKeyFrame?> onFound = null) where T : KeyFrame
+    private bool TryFindKeyFrame<T>(Guid id, out T? foundKeyFrame, Action<KeyFrame, GroupKeyFrame?> onFound = null) where T : IReadOnlyKeyFrame
     {
         return TryFindKeyFrame(keyFrames, null, id, out foundKeyFrame, onFound);
     }
 
-    private bool TryFindKeyFrame<T>(List<KeyFrame> root, GroupKeyFrame parent, Guid id, out T result, Action<KeyFrame, GroupKeyFrame?> onFound) where T : KeyFrame
+    private bool TryFindKeyFrame<T>(List<KeyFrame> root, GroupKeyFrame parent, Guid id, out T? result, Action<KeyFrame, GroupKeyFrame?> onFound) where T : IReadOnlyKeyFrame
     {
         for (var i = 0; i < root.Count; i++)
         {
@@ -87,7 +87,7 @@ public class AnimationData : IReadOnlyAnimationData
             }
         }
 
-        result = null;
+        result = default;
         return false;
     }
     
@@ -119,6 +119,7 @@ public class AnimationData : IReadOnlyAnimationData
                     }
                     else
                     {
+                        Console.WriteLine($"{ActiveFrame}");
                         keyFrame.ActiveFrameChanged(ActiveFrame);
                     }
                 }

+ 1 - 2
src/PixiEditor.ChangeableDocument/Changeables/Animations/RasterKeyFrame.cs

@@ -19,7 +19,6 @@ internal class RasterKeyFrame : KeyFrame
     {
         if (Document.TryFindMember<RasterLayer>(LayerGuid, out var layer))
         {
-            originalLayerImage = layer.LayerImage;
             layer.LayerImage = Image;
         }
     }
@@ -28,7 +27,7 @@ internal class RasterKeyFrame : KeyFrame
     {
         if (Document.TryFindMember<RasterLayer>(LayerGuid, out var layer))
         {
-            layer.LayerImage = originalLayerImage;
+            //layer.LayerImage = originalLayerImage;
         }
     }
 }

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyAnimationData.cs

@@ -4,4 +4,5 @@ public interface IReadOnlyAnimationData
 {
     public int ActiveFrame { get; }
     public IReadOnlyList<IReadOnlyKeyFrame> KeyFrames { get; }
+    public bool FindKeyFrame(Guid id, out IReadOnlyKeyFrame keyFrame);
 }

+ 2 - 3
src/PixiEditor.ChangeableDocument/Changes/Animation/CreateRasterClip_Change.cs → src/PixiEditor.ChangeableDocument/Changes/Animation/CreateRasterKeyFrame_Change.cs

@@ -3,7 +3,7 @@ using PixiEditor.ChangeableDocument.ChangeInfos.Animation;
 
 namespace PixiEditor.ChangeableDocument.Changes.Animation;
 
-internal class CreateRasterClip_Change : Change
+internal class CreateRasterKeyFrame_Change : Change
 {
     private readonly Guid _targetLayerGuid;
     private readonly int _frame;
@@ -12,7 +12,7 @@ internal class CreateRasterClip_Change : Change
     private Guid createdKeyFrameId;
 
     [GenerateMakeChangeAction]
-    public CreateRasterClip_Change(Guid targetLayerGuid, int frame, bool cloneFromExisting = false)
+    public CreateRasterKeyFrame_Change(Guid targetLayerGuid, int frame, bool cloneFromExisting = false)
     {
         _targetLayerGuid = targetLayerGuid;
         _frame = frame;
@@ -30,7 +30,6 @@ internal class CreateRasterClip_Change : Change
             new RasterKeyFrame(_targetLayerGuid, _frame, target, _cloneFromExisting ? _layer.LayerImage : null);
         createdKeyFrameId = keyFrame.Id;
         target.AnimationData.AddKeyFrame(keyFrame);
-        target.AnimationData.ChangePreviewFrame(_frame);
         ignoreInUndo = false;
         return new CreateRasterKeyFrame_ChangeInfo(_targetLayerGuid, _frame, createdKeyFrameId, _cloneFromExisting);
     }