Browse Source

Create cel on next empty spot

flabbet 7 months ago
parent
commit
d4118690f1

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

@@ -4,7 +4,7 @@ using PixiEditor.ChangeableDocument.ChangeInfos.Animation;
 
 
 namespace PixiEditor.ChangeableDocument.Changes.Animation;
 namespace PixiEditor.ChangeableDocument.Changes.Animation;
 
 
-internal class CreateRasterKeyFrame_Change : Change
+internal class CreateCel_Change : Change
 {
 {
     private readonly Guid _targetLayerGuid;
     private readonly Guid _targetLayerGuid;
     private int _frame;
     private int _frame;
@@ -14,7 +14,7 @@ internal class CreateRasterKeyFrame_Change : Change
     private Guid createdKeyFrameId;
     private Guid createdKeyFrameId;
 
 
     [GenerateMakeChangeAction]
     [GenerateMakeChangeAction]
-    public CreateRasterKeyFrame_Change(Guid targetLayerGuid, Guid newKeyFrameGuid, int frame,
+    public CreateCel_Change(Guid targetLayerGuid, Guid newKeyFrameGuid, int frame,
         int cloneFromFrame = -1,
         int cloneFromFrame = -1,
         Guid cloneFromExisting = default)
         Guid cloneFromExisting = default)
     {
     {

+ 6 - 2
src/PixiEditor/Data/Localization/Languages/en.json

@@ -791,6 +791,10 @@
   "STROKE_CAP": "Stroke Cap",
   "STROKE_CAP": "Stroke Cap",
   "STROKE_JOIN": "Stroke Join",
   "STROKE_JOIN": "Stroke Join",
   "COPY_VISIBLE": "Copy visible",
   "COPY_VISIBLE": "Copy visible",
-    "COPY_VISIBLE_DESCRIPTIVE": "Copy visible pixels",
-  "COLOR_SAMPLE_MODE": "Sample mode"
+  "COPY_VISIBLE_DESCRIPTIVE": "Copy visible pixels",
+  "COLOR_SAMPLE_MODE": "Sample mode",
+  "CREATE_CEL": "Create cel",
+  "CREATE_CEL_DESCRIPTIVE": "Create a new cel",
+  "DUPLICATE_CEL": "Duplicate cel",
+  "DUPLICATE_CEL_DESCRIPTIVE": "Duplicate cel in the current frame",
 }
 }

+ 1 - 1
src/PixiEditor/Models/Handlers/IAnimationHandler.cs

@@ -11,7 +11,7 @@ internal interface IAnimationHandler
     public int OnionFramesBindable { get; set; }
     public int OnionFramesBindable { get; set; }
     public double OnionOpacityBindable { get; set; }
     public double OnionOpacityBindable { get; set; }
     public bool IsPlayingBindable { get; set; }
     public bool IsPlayingBindable { get; set; }
-    public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null, int? frameToCopyFrom = null);
+    public void CreateCel(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null, int? frameToCopyFrom = null);
     public void SetFrameRate(int newFrameRate);
     public void SetFrameRate(int newFrameRate);
     public void SetActiveFrame(int newFrame);
     public void SetActiveFrame(int newFrame);
     public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration);
     public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration);

+ 3 - 3
src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs

@@ -111,7 +111,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         ? keyFrames.Max(x => x.StartFrameBindable + x.DurationBindable)
         ? keyFrames.Max(x => x.StartFrameBindable + x.DurationBindable)
         : DefaultEndFrame;
         : DefaultEndFrame;
 
 
-    public int FramesCount => LastFrame - FirstFrame + 1;
+    public int FramesCount => LastFrame - FirstFrame;
 
 
     private double ActiveNormalizedTime => (double)(ActiveFrameBindable - FirstFrame) / FramesCount;
     private double ActiveNormalizedTime => (double)(ActiveFrameBindable - FirstFrame) / FramesCount;
 
 
@@ -126,12 +126,12 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public KeyFrameTime ActiveFrameTime => new KeyFrameTime(ActiveFrameBindable, ActiveNormalizedTime);
     public KeyFrameTime ActiveFrameTime => new KeyFrameTime(ActiveFrameBindable, ActiveNormalizedTime);
 
 
-    public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null,
+    public void CreateCel(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null,
         int? frameToCopyFrom = null)
         int? frameToCopyFrom = null)
     {
     {
         if (!Document.BlockingUpdateableChangeActive)
         if (!Document.BlockingUpdateableChangeActive)
         {
         {
-            Internals.ActionAccumulator.AddFinishedActions(new CreateRasterKeyFrame_Action(targetLayerGuid,
+            Internals.ActionAccumulator.AddFinishedActions(new CreateCel_Action(targetLayerGuid,
                 Guid.NewGuid(), Math.Max(1, frame),
                 Guid.NewGuid(), Math.Max(1, frame),
                 frameToCopyFrom ?? -1, toCloneFrom ?? Guid.Empty));
                 frameToCopyFrom ?? -1, toCloneFrom ?? Guid.Empty));
         }
         }

+ 1 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -445,7 +445,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                     foreach (var child in group.Children)
                     foreach (var child in group.Children)
                     {
                     {
                         acc.AddActions(
                         acc.AddActions(
-                            new CreateRasterKeyFrame_Action(
+                            new CreateCel_Action(
                                 mappedIds[child.NodeId],
                                 mappedIds[child.NodeId],
                                 mappedKeyFrameIds[child.KeyFrameId],
                                 mappedKeyFrameIds[child.KeyFrameId],
                                 -1, -1, default));
                                 -1, -1, default));

+ 23 - 17
src/PixiEditor/ViewModels/SubViewModels/AnimationsViewModel.cs

@@ -42,14 +42,15 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
         if (activeDocument is null)
         if (activeDocument is null)
             return;
             return;
 
 
-        activeDocument.AnimationDataViewModel.IsPlayingBindable = !activeDocument.AnimationDataViewModel.IsPlayingBindable;
+        activeDocument.AnimationDataViewModel.IsPlayingBindable =
+            !activeDocument.AnimationDataViewModel.IsPlayingBindable;
     }
     }
 
 
-    [Command.Basic("PixiEditor.Animation.CreateRasterKeyFrame", "Create Raster Key Frame", "Create a raster key frame",
+    [Command.Basic("PixiEditor.Animation.CreateCel", "CREATE_CEL", "CREATE_CEL_DESCRIPTIVE",
         Parameter = false, AnalyticsTrack = true)]
         Parameter = false, AnalyticsTrack = true)]
-    [Command.Basic("PixiEditor.Animation.DuplicateRasterKeyFrame", "Duplicate Raster Key Frame",
-        "Duplicate a raster key frame", Parameter = true, AnalyticsTrack = true)]
-    public void CreateRasterKeyFrame(bool duplicate)
+    [Command.Basic("PixiEditor.Animation.DuplicateCel", "DUPLICATE_CEL",
+        "DUPLICATE_CEL_DESCRIPTIVE", Parameter = true, AnalyticsTrack = true)]
+    public void CreateCel(bool duplicate)
     {
     {
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (activeDocument?.SelectedStructureMember is null)
         if (activeDocument?.SelectedStructureMember is null)
@@ -57,12 +58,12 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
             return;
             return;
         }
         }
 
 
-        int newFrame = GetActiveFrame(activeDocument, activeDocument.SelectedStructureMember.Id);
+        int newFrame = GetFirstEmptyFrame(activeDocument, activeDocument.SelectedStructureMember.Id);
 
 
         Guid toCloneFrom = duplicate ? activeDocument.SelectedStructureMember.Id : Guid.Empty;
         Guid toCloneFrom = duplicate ? activeDocument.SelectedStructureMember.Id : Guid.Empty;
         int frameToCopyFrom = duplicate ? activeDocument.AnimationDataViewModel.ActiveFrameBindable : -1;
         int frameToCopyFrom = duplicate ? activeDocument.AnimationDataViewModel.ActiveFrameBindable : -1;
 
 
-        activeDocument.AnimationDataViewModel.CreateRasterKeyFrame(
+        activeDocument.AnimationDataViewModel.CreateCel(
             activeDocument.SelectedStructureMember.Id,
             activeDocument.SelectedStructureMember.Id,
             newFrame,
             newFrame,
             toCloneFrom,
             toCloneFrom,
@@ -132,23 +133,28 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
     }
     }
 
 
 
 
-    private static int GetActiveFrame(DocumentViewModel activeDocument, Guid targetLayer)
+    private static int GetFirstEmptyFrame(DocumentViewModel activeDocument, Guid targetLayer)
     {
     {
         int active = activeDocument.AnimationDataViewModel.ActiveFrameBindable;
         int active = activeDocument.AnimationDataViewModel.ActiveFrameBindable;
-        if (activeDocument.AnimationDataViewModel.TryFindCels<CelGroupViewModel>(targetLayer,
+        if (activeDocument.AnimationDataViewModel.TryFindCels(targetLayer,
                 out CelGroupViewModel groupViewModel))
                 out CelGroupViewModel groupViewModel))
         {
         {
-            if (groupViewModel.Children.All(x => !x.IsWithinRange(active )))
+            if (groupViewModel.Children.All(x => !x.IsWithinRange(active)))
             {
             {
                 return active;
                 return active;
             }
             }
-            
-            if (groupViewModel.Children.All(x => !x.IsWithinRange(active + 1)))
+
+            for (int i = active + 1; i < activeDocument.AnimationDataViewModel.FramesCount; i++)
             {
             {
-                return active + 1;
+                if (groupViewModel.Children.All(x => !x.IsWithinRange(i)))
+                {
+                    return i;
+                }
             }
             }
-        }
 
 
+            return activeDocument.AnimationDataViewModel.FramesCount + 1;
+        }
+        
         return active;
         return active;
     }
     }
 
 
@@ -191,14 +197,14 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
 
 
         document.Operations.SetActiveFrame((int)value);
         document.Operations.SetActiveFrame((int)value);
     }
     }
-    
+
     private bool IsTransforming()
     private bool IsTransforming()
     {
     {
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (activeDocument is null)
         if (activeDocument is null)
             return false;
             return false;
-        
+
         return activeDocument.TransformViewModel.TransformActive || activeDocument.LineToolOverlayViewModel.IsEnabled
         return activeDocument.TransformViewModel.TransformActive || activeDocument.LineToolOverlayViewModel.IsEnabled
-            || activeDocument.PathOverlayViewModel.IsActive;
+                                                                 || activeDocument.PathOverlayViewModel.IsActive;
     }
     }
 }
 }

+ 2 - 2
src/PixiEditor/Views/Dock/TimelineDockView.axaml

@@ -15,14 +15,14 @@
     <animations:Timeline 
     <animations:Timeline 
         KeyFrames="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.KeyFrames}" 
         KeyFrames="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.KeyFrames}" 
         ActiveFrame="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.ActiveFrameBindable, Mode=TwoWay}"
         ActiveFrame="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.ActiveFrameBindable, Mode=TwoWay}"
-        NewKeyFrameCommand="{xaml:Command PixiEditor.Animation.CreateRasterKeyFrame}"
+        NewKeyFrameCommand="{xaml:Command PixiEditor.Animation.CreateCel}"
         Fps="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.FrameRateBindable, Mode=TwoWay}"
         Fps="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.FrameRateBindable, Mode=TwoWay}"
         DefaultEndFrame="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.DefaultEndFrame}"
         DefaultEndFrame="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.DefaultEndFrame}"
         OnionSkinningEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionSkinningEnabledBindable, Mode=TwoWay}"
         OnionSkinningEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionSkinningEnabledBindable, Mode=TwoWay}"
         OnionFrames="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionFramesBindable, Mode=TwoWay}"
         OnionFrames="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionFramesBindable, Mode=TwoWay}"
         OnionOpacity="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionOpacityBindable, Mode=TwoWay}"
         OnionOpacity="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionOpacityBindable, Mode=TwoWay}"
         IsPlaying="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.IsPlayingBindable, Mode=TwoWay}"
         IsPlaying="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.IsPlayingBindable, Mode=TwoWay}"
-        DuplicateKeyFrameCommand="{xaml:Command PixiEditor.Animation.DuplicateRasterKeyFrame}"
+        DuplicateKeyFrameCommand="{xaml:Command PixiEditor.Animation.DuplicateCel}"
         DeleteKeyFrameCommand="{xaml:Command PixiEditor.Animation.DeleteCels, UseProvided=True}"
         DeleteKeyFrameCommand="{xaml:Command PixiEditor.Animation.DeleteCels, UseProvided=True}"
         ChangeKeyFramesLengthCommand="{xaml:Command PixiEditor.Animation.ChangeKeyFramesStartPos, UseProvided=True}"/>
         ChangeKeyFramesLengthCommand="{xaml:Command PixiEditor.Animation.ChangeKeyFramesStartPos, UseProvided=True}"/>
 </UserControl>
 </UserControl>