Browse Source

Merge pull request #675 from PixiEditor/fixes/02.12.24

Fixes/02.12.24
Krzysztof Krysiński 8 months ago
parent
commit
e706a1a98b
49 changed files with 376 additions and 192 deletions
  1. 1 1
      src/Drawie
  2. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Filters.cs
  3. 6 5
      src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs
  4. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelected_UpdateableChange.cs
  5. 28 3
      src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs
  6. 3 2
      src/PixiEditor.Zoombox/Zoombox.cs
  7. 10 1
      src/PixiEditor/Data/Localization/Languages/en.json
  8. 17 0
      src/PixiEditor/Helpers/Converters/EnumToLocalizedStringConverter.cs
  9. 1 1
      src/PixiEditor/Helpers/Converters/ZoomToViewportConverter.cs
  10. 1 1
      src/PixiEditor/Models/Controllers/InputDevice/SnappingController.cs
  11. 1 1
      src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs
  12. 14 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs
  13. 6 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IDelayedColorSwapFeature.cs
  14. 12 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs
  15. 6 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs
  16. 1 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs
  17. 10 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs
  18. 4 0
      src/PixiEditor/Models/ExceptionHandling/CrashReport.cs
  19. 3 3
      src/PixiEditor/Models/Handlers/IAnimationHandler.cs
  20. 9 0
      src/PixiEditor/Models/Handlers/ICelGroupHandler.cs
  21. 1 1
      src/PixiEditor/Models/Handlers/ICelHandler.cs
  22. 1 0
      src/PixiEditor/Models/Handlers/IColorsHandler.cs
  23. 0 9
      src/PixiEditor/Models/Handlers/IKeyFrameGroupHandler.cs
  24. 6 0
      src/PixiEditor/Models/Handlers/IRasterCelHandler.cs
  25. 0 6
      src/PixiEditor/Models/Handlers/IRasterKeyFrameHandler.cs
  26. 9 9
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  27. 3 3
      src/PixiEditor/Styles/Templates/Timeline.axaml
  28. 39 39
      src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs
  29. 6 6
      src/PixiEditor/ViewModels/Document/CelGroupViewModel.cs
  30. 3 3
      src/PixiEditor/ViewModels/Document/CelViewModel.cs
  31. 14 0
      src/PixiEditor/ViewModels/Document/IRasterCelViewModel.cs
  32. 8 8
      src/PixiEditor/ViewModels/Document/KeyFrameCollection.cs
  33. 0 14
      src/PixiEditor/ViewModels/Document/RasterKeyFrameViewModel.cs
  34. 1 0
      src/PixiEditor/ViewModels/Document/TransformOverlays/LineToolOverlayViewModel.cs
  35. 11 11
      src/PixiEditor/ViewModels/SubViewModels/AnimationsViewModel.cs
  36. 12 0
      src/PixiEditor/ViewModels/SubViewModels/ColorsViewModel.cs
  37. 29 12
      src/PixiEditor/ViewModels/SubViewModels/IoViewModel.cs
  38. 2 0
      src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs
  39. 3 3
      src/PixiEditor/Views/Animations/KeyFrame.cs
  40. 23 23
      src/PixiEditor/Views/Animations/Timeline.cs
  41. 2 2
      src/PixiEditor/Views/Animations/TimelineGroupHeader.cs
  42. 1 1
      src/PixiEditor/Views/Dock/TimelineDockView.axaml
  43. 2 2
      src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml
  44. 4 1
      src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs
  45. 11 6
      src/PixiEditor/Views/Overlays/LineToolOverlay/LineToolOverlay.cs
  46. 11 0
      src/PixiEditor/Views/Overlays/Overlay.cs
  47. 29 4
      src/PixiEditor/Views/Rendering/Scene.cs
  48. 1 1
      src/PixiEditor/Views/Shortcuts/KeyCombinationBox.axaml.cs
  49. 7 1
      src/PixiEditor/Views/Windows/Settings/SettingsWindow.axaml

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit c6372d7854028add4f17fe36fa98a1ab7dcd3951
+Subproject commit 3470bee10ed28168e63d19f03a1260f9a0aae2d3

+ 3 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Filters.cs

@@ -39,7 +39,8 @@ public static class Filters
         ColorFilter.CreateColorMatrix(ColorMatrix.AverageGrayscale + ColorMatrix.OpaqueAlphaOffset);
         ColorFilter.CreateColorMatrix(ColorMatrix.AverageGrayscale + ColorMatrix.OpaqueAlphaOffset);
 
 
     /// <summary>
     /// <summary>
-    ///     R,G,B values are set to 0. Alpha is set to the average of R,G,B values.
+    ///     R,G,B values are set to 0. Alpha is set to the average of R,G,B values. Multiplied by alpha
     /// </summary>
     /// </summary>
-    public static readonly ColorFilter MaskFilter = ColorFilter.CreateColorMatrix(ColorMatrix.WeightedWavelengthAlphaGrayscale);
+    public static readonly ColorFilter MaskFilter = ColorFilter.CreateLumaColor();
+
 }
 }

+ 6 - 5
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -96,8 +96,8 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             {
             {
                 ApplySoftnessGradient((VecD)point);
                 ApplySoftnessGradient((VecD)point);
             }
             }
-
-            image.EnqueueDrawEllipse(rect, color, color, 1, 0, antiAliasing, srcPaint);
+            
+            image.EnqueueDrawEllipse(rect, color, color, 0, 0, antiAliasing, srcPaint);
         }
         }
 
 
         var affChunks = image.FindAffectedArea(opCount);
         var affChunks = image.FindAffectedArea(opCount);
@@ -130,18 +130,19 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
                 ApplySoftnessGradient(points[i]);
                 ApplySoftnessGradient(points[i]);
             }
             }
 
 
-            targetImage.EnqueueDrawEllipse(rect, color, color, 1, 0, antiAliasing, srcPaint);
+            targetImage.EnqueueDrawEllipse(rect, color, color, 0, 0, antiAliasing, srcPaint);
         }
         }
     }
     }
 
 
     private void ApplySoftnessGradient(VecD pos)
     private void ApplySoftnessGradient(VecD pos)
     {
     {
+        if (hardness >= 1) return;
         srcPaint.Shader?.Dispose();
         srcPaint.Shader?.Dispose();
         float radius = strokeWidth / 2f;
         float radius = strokeWidth / 2f;
         radius = MathF.Max(1, radius);
         radius = MathF.Max(1, radius);
         srcPaint.Shader = Shader.CreateRadialGradient(
         srcPaint.Shader = Shader.CreateRadialGradient(
-            pos, radius, new Color[] { color, color.WithAlpha(0) }, 
-            new float[] { hardness, 1 }, ShaderTileMode.Clamp);
+            pos, radius, [color, color.WithAlpha(0)],
+            [hardness - 0.04f, 1f], ShaderTileMode.Clamp);
     }
     }
 
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelected_UpdateableChange.cs

@@ -12,7 +12,7 @@ using Drawie.Numerics;
 
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 
 
-internal class TransformSelected_UpdateableChange : UpdateableChange
+internal class TransformSelected_UpdateableChange : InterruptableUpdateableChange
 {
 {
     private readonly bool drawOnMask;
     private readonly bool drawOnMask;
     private bool keepOriginal;
     private bool keepOriginal;

+ 28 - 3
src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs

@@ -203,11 +203,30 @@ public class DocumentChangeTracker : IDisposable
     private OneOf<None, IChangeInfo, List<IChangeInfo>> ProcessMakeChangeAction(IMakeChangeAction act,
     private OneOf<None, IChangeInfo, List<IChangeInfo>> ProcessMakeChangeAction(IMakeChangeAction act,
         ActionSource source)
         ActionSource source)
     {
     {
-        if (activeUpdateableChange is not null)
+        if (activeUpdateableChange is not null && activeUpdateableChange is not InterruptableUpdateableChange)
         {
         {
             Trace.WriteLine($"Attempted to execute make change action {act} while {activeUpdateableChange} is active");
             Trace.WriteLine($"Attempted to execute make change action {act} while {activeUpdateableChange} is active");
             return new None();
             return new None();
         }
         }
+        
+        bool ignoreInUndo = false;
+        List<IChangeInfo> changeInfos = new();
+
+        if (activeUpdateableChange is InterruptableUpdateableChange interruptable)
+        {
+            var applyInfo = interruptable.Apply(document, false, out ignoreInUndo);
+            if (!ignoreInUndo)
+                AddToUndo(interruptable, source);
+            else
+                interruptable.Dispose();
+            
+            applyInfo.Switch(
+                static (None _) => { },
+                (IChangeInfo info) => changeInfos.Add(info),
+                (List<IChangeInfo> infos) => changeInfos.AddRange(infos));
+            
+            activeUpdateableChange = null;
+        }
 
 
         var change = act.CreateCorrespondingChange();
         var change = act.CreateCorrespondingChange();
         var validationResult = change.InitializeAndValidate(document);
         var validationResult = change.InitializeAndValidate(document);
@@ -218,12 +237,18 @@ public class DocumentChangeTracker : IDisposable
             return new None();
             return new None();
         }
         }
 
 
-        var info = change.Apply(document, true, out bool ignoreInUndo);
+        var info = change.Apply(document, true, out ignoreInUndo);
+        
+        info.Switch(
+            static (None _) => { },
+            (IChangeInfo changeInfo) => changeInfos.Add(changeInfo),
+            (List<IChangeInfo> infos) => changeInfos.AddRange(infos));
+        
         if (!ignoreInUndo)
         if (!ignoreInUndo)
             AddToUndo(change, source);
             AddToUndo(change, source);
         else
         else
             change.Dispose();
             change.Dispose();
-        return info;
+        return changeInfos;
     }
     }
 
 
     private OneOf<None, IChangeInfo, List<IChangeInfo>> ProcessStartOrUpdateChangeAction(IStartOrUpdateChangeAction act,
     private OneOf<None, IChangeInfo, List<IChangeInfo>> ProcessStartOrUpdateChangeAction(IStartOrUpdateChangeAction act,

+ 3 - 2
src/PixiEditor.Zoombox/Zoombox.cs

@@ -158,6 +158,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
     }
     }
 
 
     internal const double ScaleFactor = 1.09050773267; //2^(1/8)
     internal const double ScaleFactor = 1.09050773267; //2^(1/8)
+    internal const double ScrollStep = 0.5;
 
 
     public VecD ToScreenSpace(VecD p)
     public VecD ToScreenSpace(VecD p)
     {
     {
@@ -429,10 +430,10 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
 
 
     private void OnScroll(object? sender, PointerWheelEventArgs e)
     private void OnScroll(object? sender, PointerWheelEventArgs e)
     {
     {
-        double abs = Math.Abs(e.Delta.Y / 100.0);
+        double abs = Math.Abs(e.Delta.Y / ScrollStep);
         for (int i = 0; i < abs; i++)
         for (int i = 0; i < abs; i++)
         {
         {
-            ZoomInto(ToVecD(e.GetPosition(this)), e.Delta.Y / 100.0);
+            ZoomInto(ToVecD(e.GetPosition(this)), e.Delta.Y / ScrollStep);
         }
         }
     }
     }
 
 

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

@@ -769,5 +769,14 @@
   "PATH_TOOL_TOOLTIP": "Create vector paths and curves ({0}).",
   "PATH_TOOL_TOOLTIP": "Create vector paths and curves ({0}).",
   "PATH_TOOL_ACTION_DISPLAY": "Click to add a point.",
   "PATH_TOOL_ACTION_DISPLAY": "Click to add a point.",
   "PATH_TOOL_ACTION_DISPLAY_CTRL": "Click on existing point and drag to make it a curve.", 
   "PATH_TOOL_ACTION_DISPLAY_CTRL": "Click on existing point and drag to make it a curve.", 
-  "PATH_TOOL_ACTION_DISPLAY_ALT": "Click on a control point and move to adjust only one side of the curve."
+  "PATH_TOOL_ACTION_DISPLAY_ALT": "Click on a control point and move to adjust only one side of the curve.",
+  "DEFAULT_PATH_LAYER_NAME": "Path",
+  "DELETE_NODES": "Delete nodes",
+  "DELETE_NODES_DESCRIPTIVE": "Delete selected nodes",
+  "DELETE_CELS": "Delete cels",
+  "DELETE_CELS_DESCRIPTIVE": "Delete selected cels",
+  "COPY_COLOR_TO_CLIPBOARD": "Copy color to clipboard",
+  "VIEWPORT_ROTATION": "Viewport rotation",
+  "NEXT_TOOL_SET": "Next tool set",
+  "PREVIOUS_TOOL_SET": "Previous tool set"
 }
 }

+ 17 - 0
src/PixiEditor/Helpers/Converters/EnumToLocalizedStringConverter.cs

@@ -0,0 +1,17 @@
+using System.Globalization;
+using PixiEditor.Extensions.Helpers;
+
+namespace PixiEditor.Helpers.Converters;
+
+internal class EnumToLocalizedStringConverter : SingleInstanceConverter<EnumToLocalizedStringConverter>
+{
+    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is Enum enumValue)
+        {
+            return EnumHelpers.GetDescription(enumValue);
+        }
+
+        return value;
+    }
+}

+ 1 - 1
src/PixiEditor/Helpers/Converters/ZoomToViewportConverter.cs

@@ -19,7 +19,7 @@ internal class ZoomToViewportConverter
 
 
     public static double ZoomToViewport(double factor, double scale)
     public static double ZoomToViewport(double factor, double scale)
     {
     {
-        double newSize = Math.Clamp(factor / scale, 2, 9999);
+        double newSize = Math.Clamp(factor / scale, 1, 9999);
 
 
         double log = Math.Log(newSize, 2);
         double log = Math.Log(newSize, 2);
         //round to power of 2
         //round to power of 2

+ 1 - 1
src/PixiEditor/Models/Controllers/InputDevice/SnappingController.cs

@@ -277,7 +277,7 @@ public class SnappingController
             return pos;
             return pos;
         }
         }
 
 
-        if (direction == VecD.Zero)
+        if (direction.X == 0 || direction.Y == 0)
         {
         {
             return GetSnapPoint(pos, out xAxis, out yAxis);
             return GetSnapPoint(pos, out xAxis, out yAxis);
         }
         }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs

@@ -465,7 +465,7 @@ internal class DocumentUpdater
 
 
     private void ProcessCreateRasterKeyFrame(CreateRasterKeyFrame_ChangeInfo info)
     private void ProcessCreateRasterKeyFrame(CreateRasterKeyFrame_ChangeInfo info)
     {
     {
-        var vm = new RasterKeyFrameViewModel(info.TargetLayerGuid, info.Frame, 1,
+        var vm = new IRasterCelViewModel(info.TargetLayerGuid, info.Frame, 1,
             info.KeyFrameId,
             info.KeyFrameId,
             (DocumentViewModel)doc, helper);
             (DocumentViewModel)doc, helper);
         
         

+ 14 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs

@@ -10,6 +10,7 @@ using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 
@@ -33,6 +34,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     private bool noMovement = true;
     private bool noMovement = true;
     protected IBasicShapeToolbar toolbar;
     protected IBasicShapeToolbar toolbar;
     private IColorsHandler? colorsVM;
     private IColorsHandler? colorsVM;
+    private bool ignoreNextColorChange = false;
 
 
     public override bool CanUndo => document.TransformHandler.HasUndo;
     public override bool CanUndo => document.TransformHandler.HasUndo;
     public override bool CanRedo => document.TransformHandler.HasRedo;
     public override bool CanRedo => document.TransformHandler.HasRedo;
@@ -60,6 +62,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
             {
             {
                 toolbar.FillColor = colorsVM.PrimaryColor.ToColor();
                 toolbar.FillColor = colorsVM.PrimaryColor.ToColor();
                 toolbar.StrokeColor = colorsVM.PrimaryColor.ToColor();
                 toolbar.StrokeColor = colorsVM.PrimaryColor.ToColor();
+                ignoreNextColorChange = colorsVM.ColorsTempSwapped;
             }
             }
             
             
             lastRect = new RectD(startDrawingPos, VecD.Zero);
             lastRect = new RectD(startDrawingPos, VecD.Zero);
@@ -161,11 +164,19 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
 
     public override void OnColorChanged(Color color, bool primary)
     public override void OnColorChanged(Color color, bool primary)
     {
     {
-        if (primary && toolbar.SyncWithPrimaryColor && ActiveMode == ShapeToolMode.Transform)
+        if (!primary || !toolbar.SyncWithPrimaryColor || ActiveMode == ShapeToolMode.Preview || ignoreNextColorChange)
         {
         {
-            toolbar.StrokeColor = color.ToColor();
-            toolbar.FillColor = color.ToColor();
+            if (primary)
+            {
+                ignoreNextColorChange = false;
+            }
+            return;
         }
         }
+        
+        ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
+
+        toolbar.StrokeColor = color.ToColor();
+        toolbar.FillColor = color.ToColor();
     }
     }
 
 
     public override void OnSelectedObjectNudged(VecI distance)
     public override void OnSelectedObjectNudged(VecI distance)

+ 6 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IDelayedColorSwapFeature.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+
+public interface IDelayedColorSwapFeature : IExecutorFeature
+{
+    
+}

+ 12 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -10,6 +10,7 @@ using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers.InputDevice;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 
@@ -27,6 +28,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     private T? toolViewModel;
     private T? toolViewModel;
     private IColorsHandler? colorsVM;
     private IColorsHandler? colorsVM;
     protected ILineToolbar? toolbar;
     protected ILineToolbar? toolbar;
+    private bool ignoreNextColorChange = false;
 
 
     public override bool CanUndo => document.LineToolOverlayHandler.HasUndo;
     public override bool CanUndo => document.LineToolOverlayHandler.HasUndo;
     public override bool CanRedo => document.LineToolOverlayHandler.HasRedo;
     public override bool CanRedo => document.LineToolOverlayHandler.HasRedo;
@@ -54,8 +56,10 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
             if (toolbar.SyncWithPrimaryColor)
             if (toolbar.SyncWithPrimaryColor)
             {
             {
                 toolbar.StrokeColor = colorsVM.PrimaryColor.ToColor();
                 toolbar.StrokeColor = colorsVM.PrimaryColor.ToColor();
+                ignoreNextColorChange = colorsVM.ColorsTempSwapped;
             }
             }
 
 
+            document.LineToolOverlayHandler.Hide();
             document.LineToolOverlayHandler.Show(startDrawingPos, startDrawingPos, false);
             document.LineToolOverlayHandler.Show(startDrawingPos, startDrawingPos, false);
             document.LineToolOverlayHandler.ShowHandles = false;
             document.LineToolOverlayHandler.ShowHandles = false;
             document.LineToolOverlayHandler.IsSizeBoxEnabled = true;
             document.LineToolOverlayHandler.IsSizeBoxEnabled = true;
@@ -155,9 +159,16 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
 
 
     public override void OnColorChanged(Color color, bool primary)
     public override void OnColorChanged(Color color, bool primary)
     {
     {
-        if (!primary || !toolbar!.SyncWithPrimaryColor || ActiveMode != ShapeToolMode.Transform)
+        if (!primary || !toolbar!.SyncWithPrimaryColor || ActiveMode == ShapeToolMode.Preview || ignoreNextColorChange)
+        {
+            if (primary)
+            {
+                ignoreNextColorChange = false;
+            }
             return;
             return;
+        }
 
 
+        ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
         toolbar!.StrokeColor = color.ToColor();
         toolbar!.StrokeColor = color.ToColor();
         var colorChangedAction = SettingsChange();
         var colorChangedAction = SettingsChange();
         internals!.ActionAccumulator.AddActions(colorChangedAction);
         internals!.ActionAccumulator.AddActions(colorChangedAction);

+ 6 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs

@@ -26,7 +26,7 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 ///         - Transform -> Drawing (when user clicks outside of shape transform bounds)
 ///         - Transform -> Drawing (when user clicks outside of shape transform bounds)
 /// </summary>
 /// </summary>
 internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor, 
 internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor, 
-    ITransformableExecutor, IMidChangeUndoableExecutor
+    ITransformableExecutor, IMidChangeUndoableExecutor, IDelayedColorSwapFeature
 {
 {
     private ShapeToolMode activeMode;
     private ShapeToolMode activeMode;
 
 
@@ -228,6 +228,11 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
             return ActiveMode == ShapeToolMode.Transform;
             return ActiveMode == ShapeToolMode.Transform;
         }
         }
         
         
+        if (feature is IDelayedColorSwapFeature)
+        {
+            return true;
+        }
+        
         return false;
         return false;
     }
     }
 }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs

@@ -14,7 +14,7 @@ internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor, ITran
             return ExecutionState.Error;
             return ExecutionState.Error;
 
 
         ShapeCorners corners = document.ReferenceLayerHandler.ReferenceShapeBindable;
         ShapeCorners corners = document.ReferenceLayerHandler.ReferenceShapeBindable;
-        document.TransformHandler.ShowTransform(DocumentTransformMode.Scale_Rotate_Shear_NoPerspective, true, corners, true);
+        document.TransformHandler.ShowTransform(DocumentTransformMode.Scale_Rotate_Shear_Perspective, true, corners, true);
         document.ReferenceLayerHandler.IsTransforming = true;
         document.ReferenceLayerHandler.IsTransforming = true;
         internals!.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(corners));
         internals!.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(corners));
         return ExecutionState.Success;
         return ExecutionState.Success;

+ 10 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs

@@ -196,6 +196,16 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
 
 
     private PathVectorData ConstructShapeData()
     private PathVectorData ConstructShapeData()
     {
     {
+        if(startingPath == null)
+        {
+            return new PathVectorData(new VectorPath())
+            {
+                StrokeWidth = toolbar.ToolSize,
+                StrokeColor = toolbar.StrokeColor.ToColor(),
+                FillColor = toolbar.Fill ? toolbar.FillColor.ToColor() : Colors.Transparent,
+            };
+        }
+        
         return new PathVectorData(new VectorPath(startingPath))
         return new PathVectorData(new VectorPath(startingPath))
         {
         {
             StrokeWidth = toolbar.ToolSize,
             StrokeWidth = toolbar.ToolSize,

+ 4 - 0
src/PixiEditor/Models/ExceptionHandling/CrashReport.cs

@@ -341,7 +341,11 @@ internal class CrashReport : IDisposable
         Process process = new();
         Process process = new();
 
 
         //TODO: Handle different name for the executable, .Desktop.exe
         //TODO: Handle different name for the executable, .Desktop.exe
+#if DEBUG
+        string fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".Desktop.exe";
+        #else
         string fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".exe";
         string fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".exe";
+#endif
 
 
         process.StartInfo = new()
         process.StartInfo = new()
         {
         {

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

@@ -4,7 +4,7 @@ namespace PixiEditor.Models.Handlers;
 
 
 internal interface IAnimationHandler
 internal interface IAnimationHandler
 {
 {
-    public IReadOnlyCollection<IKeyFrameHandler> KeyFrames { get; }
+    public IReadOnlyCollection<ICelHandler> KeyFrames { get; }
     public int ActiveFrameBindable { get; set; }
     public int ActiveFrameBindable { get; set; }
     public KeyFrameTime ActiveFrameTime { get; }
     public KeyFrameTime ActiveFrameTime { get; }
     public bool OnionSkinningEnabledBindable { get; set; }
     public bool OnionSkinningEnabledBindable { get; set; }
@@ -16,8 +16,8 @@ internal interface IAnimationHandler
     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);
     public void SetKeyFrameVisibility(Guid infoKeyFrameId, bool infoIsVisible);
     public void SetKeyFrameVisibility(Guid infoKeyFrameId, bool infoIsVisible);
-    public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : IKeyFrameHandler;
-    internal void AddKeyFrame(IKeyFrameHandler keyFrame);
+    public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : ICelHandler;
+    internal void AddKeyFrame(ICelHandler iCel);
     internal void RemoveKeyFrame(Guid keyFrameId);
     internal void RemoveKeyFrame(Guid keyFrameId);
     public void AddSelectedKeyFrame(Guid keyFrameId);
     public void AddSelectedKeyFrame(Guid keyFrameId);
     public void RemoveSelectedKeyFrame(Guid keyFrameId);
     public void RemoveSelectedKeyFrame(Guid keyFrameId);

+ 9 - 0
src/PixiEditor/Models/Handlers/ICelGroupHandler.cs

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

+ 1 - 1
src/PixiEditor/Models/Handlers/IKeyFrameHandler.cs → src/PixiEditor/Models/Handlers/ICelHandler.cs

@@ -4,7 +4,7 @@ using PixiEditor.Models.Rendering;
 
 
 namespace PixiEditor.Models.Handlers;
 namespace PixiEditor.Models.Handlers;
 
 
-internal interface IKeyFrameHandler
+internal interface ICelHandler
 {
 {
     public PreviewPainter? PreviewPainter { get; set; }
     public PreviewPainter? PreviewPainter { get; set; }
     public int StartFrameBindable { get; }
     public int StartFrameBindable { get; }

+ 1 - 0
src/PixiEditor/Models/Handlers/IColorsHandler.cs

@@ -8,5 +8,6 @@ internal interface IColorsHandler : IHandler
     public static IColorsHandler? Instance { get; }
     public static IColorsHandler? Instance { get; }
     public Color PrimaryColor { get; set; }
     public Color PrimaryColor { get; set; }
     public Color SecondaryColor { get; set; }
     public Color SecondaryColor { get; set; }
+    public bool ColorsTempSwapped { get; }
     public void AddSwatch(PaletteColor paletteColor);
     public void AddSwatch(PaletteColor paletteColor);
 }
 }

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

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

+ 6 - 0
src/PixiEditor/Models/Handlers/IRasterCelHandler.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.Handlers;
+
+internal interface IRasterCelHandler : ICelHandler
+{
+
+}

+ 0 - 6
src/PixiEditor/Models/Handlers/IRasterKeyFrameHandler.cs

@@ -1,6 +0,0 @@
-namespace PixiEditor.Models.Handlers;
-
-internal interface IRasterKeyFrameHandler : IKeyFrameHandler
-{
-
-}

+ 9 - 9
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -111,7 +111,7 @@ internal class MemberPreviewUpdater
     {
     {
         foreach (var keyFrame in doc.AnimationHandler.KeyFrames)
         foreach (var keyFrame in doc.AnimationHandler.KeyFrames)
         {
         {
-            if (keyFrame is IKeyFrameGroupHandler groupHandler)
+            if (keyFrame is ICelGroupHandler groupHandler)
             {
             {
                 foreach (var childFrame in groupHandler.Children)
                 foreach (var childFrame in groupHandler.Children)
                 {
                 {
@@ -132,22 +132,22 @@ internal class MemberPreviewUpdater
         }
         }
     }
     }
     
     
-    private bool IsInFrame(IKeyFrameHandler keyFrame)
+    private bool IsInFrame(ICelHandler iCel)
     {
     {
-        return keyFrame.StartFrameBindable <= doc.AnimationHandler.ActiveFrameBindable &&
-               keyFrame.StartFrameBindable + keyFrame.DurationBindable >= doc.AnimationHandler.ActiveFrameBindable;
+        return iCel.StartFrameBindable <= doc.AnimationHandler.ActiveFrameBindable &&
+               iCel.StartFrameBindable + iCel.DurationBindable >= doc.AnimationHandler.ActiveFrameBindable;
     }
     }
 
 
-    private void RenderFramePreview(IKeyFrameHandler keyFrame)
+    private void RenderFramePreview(ICelHandler iCel)
     {
     {
-        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(keyFrame.Id, out KeyFrame _))
+        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(iCel.Id, out KeyFrame _))
         {
         {
-            keyFrame.PreviewPainter ??= new PreviewPainter(AnimationKeyFramePreviewRenderer, keyFrame.Id.ToString());
-            keyFrame.PreviewPainter.Repaint();
+            iCel.PreviewPainter ??= new PreviewPainter(AnimationKeyFramePreviewRenderer, iCel.Id.ToString());
+            iCel.PreviewPainter.Repaint();
         }
         }
     }
     }
     
     
-    private void RenderGroupPreview(IKeyFrameGroupHandler groupHandler)
+    private void RenderGroupPreview(ICelGroupHandler groupHandler)
     {
     {
         var group = internals.Tracker.Document.AnimationData.KeyFrames.FirstOrDefault(x => x.Id == groupHandler.Id);
         var group = internals.Tracker.Document.AnimationData.KeyFrames.FirstOrDefault(x => x.Id == groupHandler.Id);
         if (group != null)
         if (group != null)

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

@@ -205,7 +205,7 @@
                                 <ItemsControl
                                 <ItemsControl
                                     ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                     ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                     <ItemsControl.DataTemplates>
                                     <ItemsControl.DataTemplates>
-                                        <DataTemplate DataType="document:KeyFrameGroupViewModel">
+                                        <DataTemplate DataType="document:CelGroupViewModel">
                                             <animations:TimelineGroupHeader Height="70"
                                             <animations:TimelineGroupHeader Height="70"
                                                                             Item="{Binding}" />
                                                                             Item="{Binding}" />
                                         </DataTemplate>
                                         </DataTemplate>
@@ -234,7 +234,7 @@
                                 <ItemsControl ClipToBounds="False" Name="PART_KeyFramesHost"
                                 <ItemsControl ClipToBounds="False" Name="PART_KeyFramesHost"
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                               ItemsSource="{Binding KeyFrames, RelativeSource={RelativeSource TemplatedParent}}">
                                     <ItemsControl.DataTemplates>
                                     <ItemsControl.DataTemplates>
-                                        <DataTemplate DataType="document:KeyFrameGroupViewModel">
+                                        <DataTemplate DataType="document:CelGroupViewModel">
                                             <ItemsControl ClipToBounds="False"
                                             <ItemsControl ClipToBounds="False"
                                                           BorderThickness="0, 0, 0, 1"
                                                           BorderThickness="0, 0, 0, 1"
                                                           BorderBrush="{DynamicResource ThemeBorderMidBrush}"
                                                           BorderBrush="{DynamicResource ThemeBorderMidBrush}"
@@ -256,7 +256,7 @@
                                                 </ItemsControl.ItemsPanel>
                                                 </ItemsControl.ItemsPanel>
                                             </ItemsControl>
                                             </ItemsControl>
                                         </DataTemplate>
                                         </DataTemplate>
-                                        <DataTemplate DataType="document:RasterKeyFrameViewModel">
+                                        <DataTemplate DataType="document:IRasterCelViewModel">
                                             <animations:KeyFrame
                                             <animations:KeyFrame
                                                 Scale="{Binding Scale, RelativeSource={RelativeSource FindAncestor, AncestorType=animations:Timeline}}"
                                                 Scale="{Binding Scale, RelativeSource={RelativeSource FindAncestor, AncestorType=animations:Timeline}}"
                                                 IsEnabled="{Binding IsVisible}"
                                                 IsEnabled="{Binding IsVisible}"

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

@@ -20,12 +20,12 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
     
     
     public DocumentViewModel Document { get; }
     public DocumentViewModel Document { get; }
     protected DocumentInternalParts Internals { get; }
     protected DocumentInternalParts Internals { get; }
-    public IReadOnlyCollection<IKeyFrameHandler> KeyFrames => keyFrames;
+    public IReadOnlyCollection<ICelHandler> KeyFrames => keyFrames;
 
 
-    public IReadOnlyCollection<IKeyFrameHandler> AllKeyFrames => allKeyFrames;
+    public IReadOnlyCollection<ICelHandler> AllCels => allCels;
 
 
     private KeyFrameCollection keyFrames = new KeyFrameCollection();
     private KeyFrameCollection keyFrames = new KeyFrameCollection();
-    private List<IKeyFrameHandler> allKeyFrames = new List<IKeyFrameHandler>();
+    private List<ICelHandler> allCels = new List<ICelHandler>();
     private bool onionSkinningEnabled;
     private bool onionSkinningEnabled;
     private bool isPlayingBindable;
     private bool isPlayingBindable;
 
 
@@ -135,7 +135,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         }
         }
     }
     }
 
 
-    public void DeleteKeyFrames(List<Guid> keyFrameIds)
+    public void DeleteCels(List<Guid> keyFrameIds)
     {
     {
         if (!Document.BlockingUpdateableChangeActive)
         if (!Document.BlockingUpdateableChangeActive)
         {
         {
@@ -213,7 +213,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration)
     public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration)
     {
     {
-        if (TryFindKeyFrame(keyFrameId, out KeyFrameViewModel keyFrame))
+        if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
         {
         {
             keyFrame.SetStartFrame(newStartFrame);
             keyFrame.SetStartFrame(newStartFrame);
             keyFrame.SetDuration(newDuration);
             keyFrame.SetDuration(newDuration);
@@ -223,34 +223,34 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void SetKeyFrameVisibility(Guid keyFrameId, bool isVisible)
     public void SetKeyFrameVisibility(Guid keyFrameId, bool isVisible)
     {
     {
-        if (TryFindKeyFrame(keyFrameId, out KeyFrameViewModel keyFrame))
+        if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
         {
         {
             keyFrame.SetVisibility(isVisible);
             keyFrame.SetVisibility(isVisible);
             keyFrames.NotifyCollectionChanged();
             keyFrames.NotifyCollectionChanged();
         }
         }
     }
     }
 
 
-    public void AddKeyFrame(IKeyFrameHandler keyFrame)
+    public void AddKeyFrame(ICelHandler iCel)
     {
     {
-        Guid id = keyFrame.LayerGuid;
-        if (TryFindKeyFrame(id, out KeyFrameGroupViewModel foundGroup))
+        Guid id = iCel.LayerGuid;
+        if (TryFindCels(id, out CelGroupViewModel foundGroup))
         {
         {
-            foundGroup.Children.Add((KeyFrameViewModel)keyFrame);
+            foundGroup.Children.Add((CelViewModel)iCel);
         }
         }
         else
         else
         {
         {
             var group =
             var group =
-                new KeyFrameGroupViewModel(keyFrame.StartFrameBindable, keyFrame.DurationBindable, id, id, Document,
+                new CelGroupViewModel(iCel.StartFrameBindable, iCel.DurationBindable, id, id, Document,
                     Internals);
                     Internals);
-            group.Children.Add((KeyFrameViewModel)keyFrame);
+            group.Children.Add((CelViewModel)iCel);
             keyFrames.Add(group);
             keyFrames.Add(group);
         }
         }
 
 
-        keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Add, (KeyFrameViewModel)keyFrame);
+        keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Add, (CelViewModel)iCel);
 
 
-        if (!allKeyFrames.Contains(keyFrame))
+        if (!allCels.Contains(iCel))
         {
         {
-            allKeyFrames.Add(keyFrame);
+            allCels.Add(iCel);
         }
         }
         
         
         SortByLayers();
         SortByLayers();
@@ -258,16 +258,16 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void RemoveKeyFrame(Guid keyFrameId)
     public void RemoveKeyFrame(Guid keyFrameId)
     {
     {
-        TryFindKeyFrame<KeyFrameViewModel>(keyFrameId, out _, (frame, parent) =>
+        TryFindCels<CelViewModel>(keyFrameId, out _, (frame, parent) =>
         {
         {
-            if (frame is not KeyFrameGroupViewModel group)
+            if (frame is not CelGroupViewModel group)
             {
             {
                 parent.Children.Remove(frame);
                 parent.Children.Remove(frame);
-                keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, (KeyFrameViewModel)frame);
+                keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, (CelViewModel)frame);
 
 
                 if (parent.Children.Count == 0)
                 if (parent.Children.Count == 0)
                 {
                 {
-                    keyFrames.Remove(parent as KeyFrameGroupViewModel);
+                    keyFrames.Remove(parent as CelGroupViewModel);
                 }
                 }
             }
             }
             else
             else
@@ -276,12 +276,12 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
             }
             }
         });
         });
 
 
-        allKeyFrames.RemoveAll(x => x.Id == keyFrameId);
+        allCels.RemoveAll(x => x.Id == keyFrameId);
     }
     }
 
 
     public void AddSelectedKeyFrame(Guid keyFrameId)
     public void AddSelectedKeyFrame(Guid keyFrameId)
     {
     {
-        if (TryFindKeyFrame(keyFrameId, out KeyFrameViewModel keyFrame))
+        if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
         {
         {
             keyFrame.IsSelected = true;
             keyFrame.IsSelected = true;
         }
         }
@@ -289,7 +289,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void RemoveSelectedKeyFrame(Guid keyFrameId)
     public void RemoveSelectedKeyFrame(Guid keyFrameId)
     {
     {
-        if (TryFindKeyFrame(keyFrameId, out KeyFrameViewModel keyFrame))
+        if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
         {
         {
             keyFrame.IsSelected = false;
             keyFrame.IsSelected = false;
         }
         }
@@ -297,7 +297,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void ClearSelectedKeyFrames()
     public void ClearSelectedKeyFrames()
     {
     {
-        var selectedFrames = keyFrames.SelectChildrenBy<KeyFrameViewModel>(x => x.IsSelected);
+        var selectedFrames = keyFrames.SelectChildrenBy<CelViewModel>(x => x.IsSelected);
         foreach (var frame in selectedFrames)
         foreach (var frame in selectedFrames)
         {
         {
             frame.IsSelected = false;
             frame.IsSelected = false;
@@ -306,36 +306,36 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void RemoveKeyFrames(List<Guid> keyFrameIds)
     public void RemoveKeyFrames(List<Guid> keyFrameIds)
     {
     {
-        List<KeyFrameViewModel> framesToRemove = new List<KeyFrameViewModel>();
+        List<CelViewModel> framesToRemove = new List<CelViewModel>();
         foreach (var keyFrame in keyFrameIds)
         foreach (var keyFrame in keyFrameIds)
         {
         {
-            TryFindKeyFrame<KeyFrameViewModel>(keyFrame, out _, (frame, parent) =>
+            TryFindCels<CelViewModel>(keyFrame, out _, (frame, parent) =>
             {
             {
                 parent.Children.Remove(frame);
                 parent.Children.Remove(frame);
-                framesToRemove.Add((KeyFrameViewModel)frame);
+                framesToRemove.Add((CelViewModel)frame);
             });
             });
 
 
-            allKeyFrames.RemoveAll(x => x.Id == keyFrame);
+            allCels.RemoveAll(x => x.Id == keyFrame);
         }
         }
 
 
         keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, framesToRemove);
         keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, framesToRemove);
     }
     }
 
 
-    public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : IKeyFrameHandler
+    public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : ICelHandler
     {
     {
-        return TryFindKeyFrame<T>(keyFrames, null, guid, out keyFrameHandler, null);
+        return TryFindCels<T>(keyFrames, null, guid, out keyFrameHandler, null);
     }
     }
 
 
     // TODO: Use the same structure functions as layers
     // TODO: Use the same structure functions as layers
-    public bool TryFindKeyFrame<T>(Guid id, out T? foundKeyFrame,
-        Action<IKeyFrameHandler, IKeyFrameGroupHandler?> onFound = null) where T : IKeyFrameHandler
+    public bool TryFindCels<T>(Guid id, out T? foundKeyFrame,
+        Action<ICelHandler, ICelGroupHandler?> onFound = null) where T : ICelHandler
     {
     {
-        return TryFindKeyFrame(keyFrames, null, id, out foundKeyFrame, onFound);
+        return TryFindCels(keyFrames, null, id, out foundKeyFrame, onFound);
     }
     }
 
 
-    private bool TryFindKeyFrame<T>(IReadOnlyCollection<IKeyFrameHandler> root, IKeyFrameGroupHandler parent, Guid id,
+    private bool TryFindCels<T>(IReadOnlyCollection<ICelHandler> root, ICelGroupHandler parent, Guid id,
         out T? result,
         out T? result,
-        Action<IKeyFrameHandler, IKeyFrameGroupHandler?> onFound) where T : IKeyFrameHandler
+        Action<ICelHandler, ICelGroupHandler?> onFound) where T : ICelHandler
     {
     {
         for (var i = 0; i < root.Count; i++)
         for (var i = 0; i < root.Count; i++)
         {
         {
@@ -347,9 +347,9 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
                 return true;
                 return true;
             }
             }
 
 
-            if (frame is IKeyFrameGroupHandler { Children.Count: > 0 } group)
+            if (frame is ICelGroupHandler { Children.Count: > 0 } group)
             {
             {
-                bool found = TryFindKeyFrame(group.Children, group, id, out result, onFound);
+                bool found = TryFindCels(group.Children, group, id, out result, onFound);
                 if (found)
                 if (found)
                 {
                 {
                     return true;
                     return true;
@@ -365,10 +365,10 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
     {
     {
         var allLayers = Document.StructureHelper.GetAllLayers();
         var allLayers = Document.StructureHelper.GetAllLayers();
         var unsortedKeyFrames = keyFrames.ToList();
         var unsortedKeyFrames = keyFrames.ToList();
-        var layerKeyFrames = new List<KeyFrameGroupViewModel>();
+        var layerKeyFrames = new List<CelGroupViewModel>();
         foreach (var layer in allLayers)
         foreach (var layer in allLayers)
         {
         {
-            var group = unsortedKeyFrames.FirstOrDefault(x => x is KeyFrameGroupViewModel group && group.LayerGuid == layer.Id) as KeyFrameGroupViewModel; 
+            var group = unsortedKeyFrames.FirstOrDefault(x => x is CelGroupViewModel group && group.LayerGuid == layer.Id) as CelGroupViewModel; 
             if(group != null)
             if(group != null)
             {
             {
                 layerKeyFrames.Insert(0, group);
                 layerKeyFrames.Insert(0, group);
@@ -377,7 +377,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
         foreach (var remaining in unsortedKeyFrames)
         foreach (var remaining in unsortedKeyFrames)
         {
         {
-            if (remaining is KeyFrameGroupViewModel group && !layerKeyFrames.Contains(group))
+            if (remaining is CelGroupViewModel group && !layerKeyFrames.Contains(group))
             {
             {
                 layerKeyFrames.Add(group);
                 layerKeyFrames.Add(group);
             }
             }

+ 6 - 6
src/PixiEditor/ViewModels/Document/KeyFrameGroupViewModel.cs → src/PixiEditor/ViewModels/Document/CelGroupViewModel.cs

@@ -6,9 +6,9 @@ using PixiEditor.Models.Handlers;
 
 
 namespace PixiEditor.ViewModels.Document;
 namespace PixiEditor.ViewModels.Document;
 
 
-internal class KeyFrameGroupViewModel : KeyFrameViewModel, IKeyFrameGroupHandler
+internal class CelGroupViewModel : CelViewModel, ICelGroupHandler
 {
 {
-    public ObservableCollection<IKeyFrameHandler> Children { get; } = new ObservableCollection<IKeyFrameHandler>();
+    public ObservableCollection<ICelHandler> Children { get; } = new ObservableCollection<ICelHandler>();
 
 
     public override int StartFrameBindable => Children.Count > 0 ? Children.Min(x => x.StartFrameBindable) : 0;
     public override int StartFrameBindable => Children.Count > 0 ? Children.Min(x => x.StartFrameBindable) : 0;
     public override int DurationBindable => Children.Count > 0 ? Children.Max(x => x.StartFrameBindable + x.DurationBindable) - StartFrameBindable : 0;
     public override int DurationBindable => Children.Count > 0 ? Children.Max(x => x.StartFrameBindable + x.DurationBindable) - StartFrameBindable : 0;
@@ -23,7 +23,7 @@ internal class KeyFrameGroupViewModel : KeyFrameViewModel, IKeyFrameGroupHandler
             SetProperty(ref _isCollapsed, value);
             SetProperty(ref _isCollapsed, value);
             foreach (var child in Children)
             foreach (var child in Children)
             {
             {
-                if (child is KeyFrameViewModel keyFrame)
+                if (child is CelViewModel keyFrame)
                 {
                 {
                     keyFrame.IsCollapsed = value;
                     keyFrame.IsCollapsed = value;
                 }
                 }
@@ -37,7 +37,7 @@ internal class KeyFrameGroupViewModel : KeyFrameViewModel, IKeyFrameGroupHandler
     {
     {
         foreach (var child in Children)
         foreach (var child in Children)
         {
         {
-            if(child is KeyFrameViewModel keyFrame)
+            if(child is CelViewModel keyFrame)
             {
             {
                 keyFrame.SetVisibility(isVisible);
                 keyFrame.SetVisibility(isVisible);
             }
             }
@@ -46,7 +46,7 @@ internal class KeyFrameGroupViewModel : KeyFrameViewModel, IKeyFrameGroupHandler
         base.SetVisibility(isVisible);
         base.SetVisibility(isVisible);
     }
     }
 
 
-    public KeyFrameGroupViewModel(int startFrame, int duration, Guid layerGuid, Guid id, DocumentViewModel doc, DocumentInternalParts internalParts) 
+    public CelGroupViewModel(int startFrame, int duration, Guid layerGuid, Guid id, DocumentViewModel doc, DocumentInternalParts internalParts) 
         : base(startFrame, duration, layerGuid, id, doc, internalParts)
         : base(startFrame, duration, layerGuid, id, doc, internalParts)
     {
     {
         Children.CollectionChanged += ChildrenOnCollectionChanged;
         Children.CollectionChanged += ChildrenOnCollectionChanged;
@@ -68,7 +68,7 @@ internal class KeyFrameGroupViewModel : KeyFrameViewModel, IKeyFrameGroupHandler
         {
         {
             foreach (var item in e.NewItems)
             foreach (var item in e.NewItems)
             {
             {
-                if (item is KeyFrameViewModel keyFrame)
+                if (item is CelViewModel keyFrame)
                 {
                 {
                     keyFrame.IsCollapsed = IsCollapsed;
                     keyFrame.IsCollapsed = IsCollapsed;
                     keyFrame.SetVisibility(IsVisible);
                     keyFrame.SetVisibility(IsVisible);

+ 3 - 3
src/PixiEditor/ViewModels/Document/KeyFrameViewModel.cs → src/PixiEditor/ViewModels/Document/CelViewModel.cs

@@ -8,7 +8,7 @@ using PixiEditor.Models.Rendering;
 
 
 namespace PixiEditor.ViewModels.Document;
 namespace PixiEditor.ViewModels.Document;
 
 
-internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
+internal abstract class CelViewModel : ObservableObject, ICelHandler
 {
 {
     private PreviewPainter? previewPainter;
     private PreviewPainter? previewPainter;
     private int startFrameBindable;
     private int startFrameBindable;
@@ -26,7 +26,7 @@ internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
     public DocumentViewModel Document { get; }
     public DocumentViewModel Document { get; }
     protected DocumentInternalParts Internals { get; }
     protected DocumentInternalParts Internals { get; }
 
 
-    IDocument IKeyFrameHandler.Document => Document;
+    IDocument ICelHandler.Document => Document;
 
 
     public PreviewPainter? PreviewPainter
     public PreviewPainter? PreviewPainter
     {
     {
@@ -93,7 +93,7 @@ internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
         set => SetProperty(ref isSelected, value);
         set => SetProperty(ref isSelected, value);
     }
     }
 
 
-    protected KeyFrameViewModel(int startFrame, int duration, Guid layerGuid, Guid id,
+    protected CelViewModel(int startFrame, int duration, Guid layerGuid, Guid id,
         DocumentViewModel document, DocumentInternalParts internalParts)
         DocumentViewModel document, DocumentInternalParts internalParts)
     {
     {
         startFrameBindable = startFrame;
         startFrameBindable = startFrame;

+ 14 - 0
src/PixiEditor/ViewModels/Document/IRasterCelViewModel.cs

@@ -0,0 +1,14 @@
+using PixiEditor.Models.DocumentModels;
+using PixiEditor.Models.Handlers;
+
+namespace PixiEditor.ViewModels.Document;
+
+internal class IRasterCelViewModel : CelViewModel, IRasterCelHandler
+{
+    public IRasterCelViewModel(Guid targetLayerGuid, int startFrame, int duration, Guid id, DocumentViewModel doc, DocumentInternalParts internalParts) 
+        : base(startFrame, duration, targetLayerGuid, id, doc, internalParts)
+    {
+        
+    }
+
+}

+ 8 - 8
src/PixiEditor/ViewModels/Document/KeyFrameCollection.cs

@@ -6,14 +6,14 @@ using PixiEditor.Views.Animations;
 
 
 namespace PixiEditor.ViewModels.Document;
 namespace PixiEditor.ViewModels.Document;
 
 
-internal class KeyFrameCollection : ObservableCollection<KeyFrameGroupViewModel>
+internal class KeyFrameCollection : ObservableCollection<CelGroupViewModel>
 {
 {
     public KeyFrameCollection()
     public KeyFrameCollection()
     {
     {
         
         
     }
     }
 
 
-    public KeyFrameCollection(IEnumerable<KeyFrameGroupViewModel> source)
+    public KeyFrameCollection(IEnumerable<CelGroupViewModel> source)
     {
     {
         foreach (var handler in source)
         foreach (var handler in source)
         {
         {
@@ -21,28 +21,28 @@ internal class KeyFrameCollection : ObservableCollection<KeyFrameGroupViewModel>
         }
         }
     }
     }
 
 
-    public event Action<KeyFrameViewModel> KeyFrameAdded; 
-    public event Action<KeyFrameViewModel> KeyFrameRemoved; 
+    public event Action<CelViewModel> KeyFrameAdded; 
+    public event Action<CelViewModel> KeyFrameRemoved; 
     
     
     public void NotifyCollectionChanged()
     public void NotifyCollectionChanged()
     {
     {
         OnPropertyChanged(new PropertyChangedEventArgs(nameof(FrameCount)));
         OnPropertyChanged(new PropertyChangedEventArgs(nameof(FrameCount)));
     }
     }
 
 
-    public void NotifyCollectionChanged(NotifyCollectionChangedAction action, KeyFrameViewModel keyFrame)
+    public void NotifyCollectionChanged(NotifyCollectionChangedAction action, CelViewModel cel)
     {
     {
         NotifyCollectionChanged();
         NotifyCollectionChanged();
         if (action == NotifyCollectionChangedAction.Add)
         if (action == NotifyCollectionChangedAction.Add)
         {
         {
-            KeyFrameAdded?.Invoke(keyFrame);
+            KeyFrameAdded?.Invoke(cel);
         }
         }
         else if (action == NotifyCollectionChangedAction.Remove)
         else if (action == NotifyCollectionChangedAction.Remove)
         {
         {
-            KeyFrameRemoved?.Invoke(keyFrame);
+            KeyFrameRemoved?.Invoke(cel);
         }
         }
     }
     }
     
     
-    public void NotifyCollectionChanged(NotifyCollectionChangedAction action, List<KeyFrameViewModel> fames)
+    public void NotifyCollectionChanged(NotifyCollectionChangedAction action, List<CelViewModel> fames)
     {
     {
         foreach (var frame in fames)
         foreach (var frame in fames)
         {
         {

+ 0 - 14
src/PixiEditor/ViewModels/Document/RasterKeyFrameViewModel.cs

@@ -1,14 +0,0 @@
-using PixiEditor.Models.DocumentModels;
-using PixiEditor.Models.Handlers;
-
-namespace PixiEditor.ViewModels.Document;
-
-internal class RasterKeyFrameViewModel : KeyFrameViewModel, IRasterKeyFrameHandler
-{
-    public RasterKeyFrameViewModel(Guid targetLayerGuid, int startFrame, int duration, Guid id, DocumentViewModel doc, DocumentInternalParts internalParts) 
-        : base(startFrame, duration, targetLayerGuid, id, doc, internalParts)
-    {
-        
-    }
-
-}

+ 1 - 0
src/PixiEditor/ViewModels/Document/TransformOverlays/LineToolOverlayViewModel.cs

@@ -106,6 +106,7 @@ internal class LineToolOverlayViewModel : ObservableObject, ILineOverlayHandler
         undoStack = null;
         undoStack = null;
         IsEnabled = false;
         IsEnabled = false;
         ShowApplyButton = false;
         ShowApplyButton = false;
+        IsSizeBoxEnabled = false;
     }
     }
 
 
     public bool Nudge(VecD distance)
     public bool Nudge(VecD distance)

+ 11 - 11
src/PixiEditor/ViewModels/SubViewModels/AnimationsViewModel.cs

@@ -75,7 +75,7 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
             "Raster",
             "Raster",
             activeDocument.AnimationDataViewModel.FrameRateBindable,
             activeDocument.AnimationDataViewModel.FrameRateBindable,
             activeDocument.AnimationDataViewModel.FramesCount,
             activeDocument.AnimationDataViewModel.FramesCount,
-            activeDocument.AnimationDataViewModel.AllKeyFrames.Count);
+            activeDocument.AnimationDataViewModel.AllCels.Count);
     }
     }
 
 
     [Command.Basic("PixiEditor.Animation.ToggleOnionSkinning", "TOGGLE_ONION_SKINNING",
     [Command.Basic("PixiEditor.Animation.ToggleOnionSkinning", "TOGGLE_ONION_SKINNING",
@@ -89,28 +89,28 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
         Owner.DocumentManagerSubViewModel.ActiveDocument?.AnimationDataViewModel.ToggleOnionSkinning(value);
         Owner.DocumentManagerSubViewModel.ActiveDocument?.AnimationDataViewModel.ToggleOnionSkinning(value);
     }
     }
 
 
-    [Command.Basic("PixiEditor.Animation.DeleteKeyFrames", "DELETE_KEY_FRAMES", "DELETE_KEY_FRAMES_DESCRIPTIVE",
+    [Command.Basic("PixiEditor.Animation.DeleteCels", "DELETE_CELS", "DELETE_CELS_DESCRIPTIVE",
         ShortcutContext = typeof(TimelineDockViewModel), Key = Key.Delete, AnalyticsTrack = true)]
         ShortcutContext = typeof(TimelineDockViewModel), Key = Key.Delete, AnalyticsTrack = true)]
-    public void DeleteKeyFrames()
+    public void DeleteCels()
     {
     {
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
-        var selected = activeDocument.AnimationDataViewModel.AllKeyFrames.Where(x => x.IsSelected).ToArray();
+        var selected = activeDocument.AnimationDataViewModel.AllCels.Where(x => x.IsSelected).ToArray();
 
 
         if (activeDocument is null || selected.Length == 0)
         if (activeDocument is null || selected.Length == 0)
             return;
             return;
 
 
-        List<Guid> keyFrameIds = selected.Select(x => x.Id).ToList();
+        List<Guid> celIds = selected.Select(x => x.Id).ToList();
 
 
-        for (int i = 0; i < keyFrameIds.Count; i++)
+        for (int i = 0; i < celIds.Count; i++)
         {
         {
-            if (!activeDocument.AnimationDataViewModel.TryFindKeyFrame<KeyFrameViewModel>(keyFrameIds[i], out _))
+            if (!activeDocument.AnimationDataViewModel.TryFindCels<CelViewModel>(celIds[i], out _))
             {
             {
-                keyFrameIds.RemoveAt(i);
+                celIds.RemoveAt(i);
                 i--;
                 i--;
             }
             }
         }
         }
 
 
-        activeDocument.AnimationDataViewModel.DeleteKeyFrames(keyFrameIds);
+        activeDocument.AnimationDataViewModel.DeleteCels(celIds);
     }
     }
 
 
     [Command.Internal("PixiEditor.Animation.ChangeKeyFramesStartPos")]
     [Command.Internal("PixiEditor.Animation.ChangeKeyFramesStartPos")]
@@ -135,8 +135,8 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
     private static int GetActiveFrame(DocumentViewModel activeDocument, Guid targetLayer)
     private static int GetActiveFrame(DocumentViewModel activeDocument, Guid targetLayer)
     {
     {
         int active = activeDocument.AnimationDataViewModel.ActiveFrameBindable;
         int active = activeDocument.AnimationDataViewModel.ActiveFrameBindable;
-        if (activeDocument.AnimationDataViewModel.TryFindKeyFrame<KeyFrameGroupViewModel>(targetLayer,
-                out KeyFrameGroupViewModel groupViewModel))
+        if (activeDocument.AnimationDataViewModel.TryFindCels<CelGroupViewModel>(targetLayer,
+                out CelGroupViewModel groupViewModel))
         {
         {
             if (active == groupViewModel.StartFrameBindable + groupViewModel.DurationBindable - 1)
             if (active == groupViewModel.StartFrameBindable + groupViewModel.DurationBindable - 1)
             {
             {

+ 12 - 0
src/PixiEditor/ViewModels/SubViewModels/ColorsViewModel.cs

@@ -50,6 +50,8 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
     public LocalPalettesFetcher LocalPaletteFetcher => _localPaletteFetcher ??=
     public LocalPalettesFetcher LocalPaletteFetcher => _localPaletteFetcher ??=
         (LocalPalettesFetcher)PaletteProvider.DataSources.FirstOrDefault(x => x is LocalPalettesFetcher)!;
         (LocalPalettesFetcher)PaletteProvider.DataSources.FirstOrDefault(x => x is LocalPalettesFetcher)!;
 
 
+    public bool ColorsTempSwapped { get; private set; }
+    
     private Color primaryColor = Colors.Black;
     private Color primaryColor = Colors.Black;
     private Color secondaryColor = Colors.White;
     private Color secondaryColor = Colors.White;
     private ColorState primaryColorState;
     private ColorState primaryColorState;
@@ -364,9 +366,19 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         Icon = PixiPerfectIcons.ColorsSwap, AnalyticsTrack = true)]
         Icon = PixiPerfectIcons.ColorsSwap, AnalyticsTrack = true)]
     public void SwapColors(object parameter)
     public void SwapColors(object parameter)
     {
     {
+        if (parameter is true)
+        {
+            ColorsTempSwapped = !ColorsTempSwapped;
+        }
+        else
+        {
+            ColorsTempSwapped = false;    
+        }
+        
         (PrimaryColor, SecondaryColor) = (SecondaryColor, PrimaryColor);
         (PrimaryColor, SecondaryColor) = (SecondaryColor, PrimaryColor);
     }
     }
 
 
+
     public void AddSwatch(PaletteColor color)
     public void AddSwatch(PaletteColor color)
     {
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;

+ 29 - 12
src/PixiEditor/ViewModels/SubViewModels/IoViewModel.cs

@@ -18,6 +18,7 @@ using PixiEditor.Models.Events;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Input;
 using PixiEditor.Models.Input;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.ViewModels.Tools.Tools;
 using PixiEditor.ViewModels.Tools.Tools;
 using PixiEditor.Views;
 using PixiEditor.Views;
@@ -26,7 +27,6 @@ namespace PixiEditor.ViewModels.SubViewModels;
 #nullable enable
 #nullable enable
 internal class IoViewModel : SubViewModel<ViewModelMain>
 internal class IoViewModel : SubViewModel<ViewModelMain>
 {
 {
-    private bool hadSwapped;
     private int? previousEraseSize;
     private int? previousEraseSize;
     private bool hadSharedToolbar;
     private bool hadSharedToolbar;
     private bool? drawingWithRight;
     private bool? drawingWithRight;
@@ -196,6 +196,11 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         activeDocument.EventInlet.OnCanvasLeftMouseButtonDown(args);
         activeDocument.EventInlet.OnCanvasLeftMouseButtonDown(args);
         Owner.ToolsSubViewModel.UseToolEventInlet(args.PositionOnCanvas, args.Button);
         Owner.ToolsSubViewModel.UseToolEventInlet(args.PositionOnCanvas, args.Button);
 
 
+        if (args.Button == MouseButton.Right)
+        {
+            HandleRightSwapColor();
+        }
+
         Analytics.SendUseTool(Owner.ToolsSubViewModel.ActiveTool, args.PositionOnCanvas, activeDocument.SizeBindable);
         Analytics.SendUseTool(Owner.ToolsSubViewModel.ActiveTool, args.PositionOnCanvas, activeDocument.SizeBindable);
     }
     }
 
 
@@ -208,17 +213,15 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         switch (tools.RightClickMode)
         switch (tools.RightClickMode)
         {
         {
             case RightClickMode.SecondaryColor when tools.ActiveTool.UsesColor:
             case RightClickMode.SecondaryColor when tools.ActiveTool.UsesColor:
-            case RightClickMode.Erase when tools.ActiveTool is ColorPickerToolViewModel:
-                if (!Owner.DocumentManagerSubViewModel.ActiveDocument.BlockingUpdateableChangeActive)
+                if (Owner.DocumentManagerSubViewModel.ActiveDocument.IsChangeFeatureActive<IDelayedColorSwapFeature>())
                 {
                 {
-                    Owner.ColorsSubViewModel.SwapColors(null);
-                }
-                else
-                {
-                    Owner.DocumentManagerSubViewModel.ActiveDocument.ToolSessionFinished += ToolSessionFinished;
+                    return true;
                 }
                 }
 
 
-                hadSwapped = true;
+                Owner.ColorsSubViewModel.SwapColors(true);
+                return true;
+            case RightClickMode.Erase when tools.ActiveTool is ColorPickerToolViewModel:
+                Owner.ColorsSubViewModel.SwapColors(true);
                 return true;
                 return true;
             case RightClickMode.Erase when tools.ActiveTool.IsErasable:
             case RightClickMode.Erase when tools.ActiveTool.IsErasable:
             {
             {
@@ -233,6 +236,22 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         }
         }
     }
     }
 
 
+    private void HandleRightSwapColor()
+    {
+        if (Owner.DocumentManagerSubViewModel.ActiveDocument is null)
+            return;
+        
+        if(Owner.ColorsSubViewModel.ColorsTempSwapped)
+            return;
+
+        var tools = Owner.ToolsSubViewModel;
+        
+        if (tools is { RightClickMode: RightClickMode.SecondaryColor, ActiveTool.UsesColor: true })
+        {
+            Owner.ColorsSubViewModel.SwapColors(true);
+        }
+    }
+    
     private void HandleRightMouseEraseDown(IToolsHandler tools)
     private void HandleRightMouseEraseDown(IToolsHandler tools)
     {
     {
         var currentToolSize = tools.ActiveTool.Toolbar.Settings.FirstOrDefault(x => x.Name == "ToolSize");
         var currentToolSize = tools.ActiveTool.Toolbar.Settings.FirstOrDefault(x => x.Name == "ToolSize");
@@ -295,8 +314,6 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         drawingWithRight = null;
         drawingWithRight = null;
 
 
         HandleRightMouseUp(button, tools);
         HandleRightMouseUp(button, tools);
-
-        hadSwapped = false;
     }
     }
 
 
     private void HandleRightMouseUp(MouseButton button, IToolsHandler tools)
     private void HandleRightMouseUp(MouseButton button, IToolsHandler tools)
@@ -306,7 +323,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
             case MouseButton.Middle:
             case MouseButton.Middle:
                 tools.RestorePreviousTool();
                 tools.RestorePreviousTool();
                 break;
                 break;
-            case MouseButton.Right when hadSwapped &&
+            case MouseButton.Right when Owner.ColorsSubViewModel.ColorsTempSwapped &&
                                         (tools.RightClickMode == RightClickMode.SecondaryColor ||
                                         (tools.RightClickMode == RightClickMode.SecondaryColor ||
                                          tools is
                                          tools is
                                          {
                                          {

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs

@@ -18,6 +18,8 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
     public override Type LayerTypeToCreateOnEmptyUse { get; } = typeof(VectorLayerNode);
     public override Type LayerTypeToCreateOnEmptyUse { get; } = typeof(VectorLayerNode);
     public override LocalizedString Tooltip => new LocalizedString("PATH_TOOL_TOOLTIP", Shortcut);
     public override LocalizedString Tooltip => new LocalizedString("PATH_TOOL_TOOLTIP", Shortcut);
 
 
+    public string? DefaultNewLayerName => new LocalizedString("DEFAULT_PATH_LAYER_NAME");
+
     public override string DefaultIcon => PixiPerfectIcons.VectorPen;
     public override string DefaultIcon => PixiPerfectIcons.VectorPen;
     public override bool StopsLinkedToolOnUse => false;
     public override bool StopsLinkedToolOnUse => false;
     public override bool IsErasable => false;
     public override bool IsErasable => false;

+ 3 - 3
src/PixiEditor/Views/Animations/KeyFrame.cs

@@ -17,7 +17,7 @@ namespace PixiEditor.Views.Animations;
 [PseudoClasses(":selected")]
 [PseudoClasses(":selected")]
 internal class KeyFrame : TemplatedControl
 internal class KeyFrame : TemplatedControl
 {
 {
-    public static readonly StyledProperty<KeyFrameViewModel> ItemProperty = AvaloniaProperty.Register<KeyFrame, KeyFrameViewModel>(
+    public static readonly StyledProperty<CelViewModel> ItemProperty = AvaloniaProperty.Register<KeyFrame, CelViewModel>(
         nameof(Item));
         nameof(Item));
 
 
     public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<KeyFrame, double>(nameof(Scale), 100);
     public static readonly StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<KeyFrame, double>(nameof(Scale), 100);
@@ -49,7 +49,7 @@ internal class KeyFrame : TemplatedControl
         set => SetValue(IsSelectedProperty, value);
         set => SetValue(IsSelectedProperty, value);
     }
     }
 
 
-    public KeyFrameViewModel Item
+    public CelViewModel Item
     {
     {
         get => GetValue(ItemProperty);
         get => GetValue(ItemProperty);
         set => SetValue(ItemProperty, value);
         set => SetValue(ItemProperty, value);
@@ -85,7 +85,7 @@ internal class KeyFrame : TemplatedControl
         _resizePanelLeft.PointerCaptureLost += UpdateKeyFrame;
         _resizePanelLeft.PointerCaptureLost += UpdateKeyFrame;
         _resizePanelRight.PointerCaptureLost += UpdateKeyFrame;
         _resizePanelRight.PointerCaptureLost += UpdateKeyFrame;
 
 
-        if (Item is not KeyFrameGroupViewModel)
+        if (Item is not CelGroupViewModel)
         {
         {
             MultiBinding marginBinding = new MultiBinding
             MultiBinding marginBinding = new MultiBinding
             {
             {

+ 23 - 23
src/PixiEditor/Views/Animations/Timeline.cs

@@ -173,8 +173,8 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
     public ICommand ClearSelectedKeyFramesCommand { get; }
     public ICommand ClearSelectedKeyFramesCommand { get; }
     public ICommand PressedKeyFrameCommand { get; }
     public ICommand PressedKeyFrameCommand { get; }
 
 
-    public IReadOnlyCollection<KeyFrameViewModel> SelectedKeyFrames => KeyFrames != null
-        ? KeyFrames.SelectChildrenBy<KeyFrameViewModel>(x => x.IsSelected).ToList()
+    public IReadOnlyCollection<CelViewModel> SelectedKeyFrames => KeyFrames != null
+        ? KeyFrames.SelectChildrenBy<CelViewModel>(x => x.IsSelected).ToList()
         : [];
         : [];
 
 
     private ToggleButton? _playToggle;
     private ToggleButton? _playToggle;
@@ -190,7 +190,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
     private Vector clickPos;
     private Vector clickPos;
     
     
     private bool shouldClearNextSelection = true;
     private bool shouldClearNextSelection = true;
-    private KeyFrameViewModel clickedKeyFrame;
+    private CelViewModel clickedCel;
     private bool dragged;
     private bool dragged;
     private int dragStartFrame;
     private int dragStartFrame;
 
 
@@ -210,12 +210,12 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
             new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000f / Fps) };
             new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(1000f / Fps) };
         _playTimer.Tick += PlayTimerOnTick;
         _playTimer.Tick += PlayTimerOnTick;
         PressedKeyFrameCommand = new RelayCommand<PointerPressedEventArgs>(KeyFramePressed);
         PressedKeyFrameCommand = new RelayCommand<PointerPressedEventArgs>(KeyFramePressed);
-        ClearSelectedKeyFramesCommand = new RelayCommand<KeyFrameViewModel>(ClearSelectedKeyFrames);
+        ClearSelectedKeyFramesCommand = new RelayCommand<CelViewModel>(ClearSelectedKeyFrames);
         DraggedKeyFrameCommand = new RelayCommand<PointerEventArgs>(KeyFramesDragged);
         DraggedKeyFrameCommand = new RelayCommand<PointerEventArgs>(KeyFramesDragged);
-        ReleasedKeyFrameCommand = new RelayCommand<KeyFrameViewModel>(KeyFramesReleased);
+        ReleasedKeyFrameCommand = new RelayCommand<CelViewModel>(KeyFramesReleased);
     }
     }
     
     
-    public void SelectKeyFrame(KeyFrameViewModel? keyFrame, bool clearSelection = true)
+    public void SelectKeyFrame(CelViewModel? keyFrame, bool clearSelection = true)
     {
     {
         if (clearSelection)
         if (clearSelection)
         {
         {
@@ -245,7 +245,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         {
         {
             ChangeKeyFramesLengthCommand.Execute((SelectedKeyFrames.Select(x => x.Id).ToArray(), 0, true));
             ChangeKeyFramesLengthCommand.Execute((SelectedKeyFrames.Select(x => x.Id).ToArray(), 0, true));
         }
         }
-        clickedKeyFrame = null;
+        clickedCel = null;
     }
     }
 
 
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
     protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
@@ -280,7 +280,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         _keyFramesHost = e.NameScope.Find<ItemsControl>("PART_KeyFramesHost");
         _keyFramesHost = e.NameScope.Find<ItemsControl>("PART_KeyFramesHost");
     }
     }
     
     
-    private void KeyFramesReleased(KeyFrameViewModel? e)
+    private void KeyFramesReleased(CelViewModel? e)
     {
     {
         if (!dragged)
         if (!dragged)
         {
         {
@@ -293,21 +293,21 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         }
         }
 
 
         dragged = false;
         dragged = false;
-        clickedKeyFrame = null;
+        clickedCel = null;
     }
     }
 
 
     private void KeyFramesDragged(PointerEventArgs? e)
     private void KeyFramesDragged(PointerEventArgs? e)
     {
     {
-        if (clickedKeyFrame == null) return;
+        if (clickedCel == null) return;
 
 
         int frameUnderMouse = MousePosToFrame(e);
         int frameUnderMouse = MousePosToFrame(e);
         int delta = frameUnderMouse - dragStartFrame;
         int delta = frameUnderMouse - dragStartFrame;
 
 
         if (delta != 0)
         if (delta != 0)
         {
         {
-            if (!clickedKeyFrame.IsSelected)
+            if (!clickedCel.IsSelected)
             {
             {
-                SelectKeyFrame(clickedKeyFrame);
+                SelectKeyFrame(clickedCel);
             }
             }
 
 
             dragged = true;
             dragged = true;
@@ -318,7 +318,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         }
         }
     }
     }
 
 
-    private void ClearSelectedKeyFrames(KeyFrameViewModel? keyFrame)
+    private void ClearSelectedKeyFrames(CelViewModel? keyFrame)
     {
     {
         ClearSelectedKeyFrames();
         ClearSelectedKeyFrames();
     }
     }
@@ -335,7 +335,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         }
         }
 
 
         e.Pointer.Capture(target);
         e.Pointer.Capture(target);
-        clickedKeyFrame = target.Item;
+        clickedCel = target.Item;
         dragStartFrame = MousePosToFrame(e);
         dragStartFrame = MousePosToFrame(e);
         e.Handled = true;
         e.Handled = true;
     }
     }
@@ -608,19 +608,19 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
         }
         }
     }
     }
 
 
-    private void KeyFrames_KeyFrameAdded(KeyFrameViewModel keyFrame)
+    private void KeyFrames_KeyFrameAdded(CelViewModel cel)
     {
     {
-        keyFrame.PropertyChanged += KeyFrameOnPropertyChanged;
+        cel.PropertyChanged += KeyFrameOnPropertyChanged;
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
     }
     }
 
 
-    private void KeyFrames_KeyFrameRemoved(KeyFrameViewModel keyFrame)
+    private void KeyFrames_KeyFrameRemoved(CelViewModel cel)
     {
     {
-        if (SelectedKeyFrames.Contains(keyFrame))
+        if (SelectedKeyFrames.Contains(cel))
         {
         {
-            keyFrame.Document.AnimationDataViewModel.RemoveSelectedKeyFrame(keyFrame.Id);
-            keyFrame.PropertyChanged -= KeyFrameOnPropertyChanged;
+            cel.Document.AnimationDataViewModel.RemoveSelectedKeyFrame(cel.Id);
+            cel.PropertyChanged -= KeyFrameOnPropertyChanged;
         }
         }
         
         
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
         PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
@@ -642,13 +642,13 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
     
     
     private void KeyFrameOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
     private void KeyFrameOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
     {
     {
-        if (sender is KeyFrameViewModel keyFrame)
+        if (sender is CelViewModel keyFrame)
         {
         {
-            if (e.PropertyName == nameof(KeyFrameViewModel.IsSelected))
+            if (e.PropertyName == nameof(CelViewModel.IsSelected))
             {
             {
                 PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
                 PropertyChanged(this, new PropertyChangedEventArgs(nameof(SelectedKeyFrames)));
             }
             }
-            else if (e.PropertyName == nameof(KeyFrameViewModel.StartFrameBindable) || e.PropertyName == nameof(KeyFrameViewModel.DurationBindable))
+            else if (e.PropertyName == nameof(CelViewModel.StartFrameBindable) || e.PropertyName == nameof(CelViewModel.DurationBindable))
             {
             {
                 PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
                 PropertyChanged(this, new PropertyChangedEventArgs(nameof(EndFrame)));
             }
             }

+ 2 - 2
src/PixiEditor/Views/Animations/TimelineGroupHeader.cs

@@ -12,10 +12,10 @@ namespace PixiEditor.Views.Animations;
 [PseudoClasses(":collapsed")]
 [PseudoClasses(":collapsed")]
 internal class TimelineGroupHeader : TemplatedControl
 internal class TimelineGroupHeader : TemplatedControl
 {
 {
-    public static readonly StyledProperty<KeyFrameGroupViewModel> ItemProperty = AvaloniaProperty.Register<TimelineGroupHeader, KeyFrameGroupViewModel>(
+    public static readonly StyledProperty<CelGroupViewModel> ItemProperty = AvaloniaProperty.Register<TimelineGroupHeader, CelGroupViewModel>(
         nameof(Item));
         nameof(Item));
 
 
-    public KeyFrameGroupViewModel Item
+    public CelGroupViewModel Item
     {
     {
         get => GetValue(ItemProperty);
         get => GetValue(ItemProperty);
         set => SetValue(ItemProperty, value);
         set => SetValue(ItemProperty, value);

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

@@ -23,6 +23,6 @@
         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.DuplicateRasterKeyFrame}"
-        DeleteKeyFrameCommand="{xaml:Command PixiEditor.Animation.DeleteKeyFrames, 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>

+ 2 - 2
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -49,7 +49,6 @@
             </EventTriggerBehavior>-->
             </EventTriggerBehavior>-->
         </Interaction.Behaviors>
         </Interaction.Behaviors>
         <overlays:TogglableFlyout Margin="5" Icon="{DynamicResource icon-tool}"
         <overlays:TogglableFlyout Margin="5" Icon="{DynamicResource icon-tool}"
-                                  ui:Translator.TooltipKey="VIEWPORT_SETTINGS"
                                   ZIndex="2" HorizontalAlignment="Right" VerticalAlignment="Top">
                                   ZIndex="2" HorizontalAlignment="Right" VerticalAlignment="Top">
             <overlays:TogglableFlyout.Child>
             <overlays:TogglableFlyout.Child>
                 <Border Padding="5"
                 <Border Padding="5"
@@ -64,6 +63,7 @@
                                     BorderThickness="{DynamicResource ThemeBorderThickness}"
                                     BorderThickness="{DynamicResource ThemeBorderThickness}"
                                     CornerRadius="{DynamicResource ControlCornerRadius}"
                                     CornerRadius="{DynamicResource ControlCornerRadius}"
                                     Background="{DynamicResource ThemeControlHighBrush}"
                                     Background="{DynamicResource ThemeControlHighBrush}"
+                                    ui:Translator.TooltipKey="VIEWPORT_ROTATION"
                                     VerticalAlignment="Center">
                                     VerticalAlignment="Center">
                                 <TextBlock TextAlignment="Center"
                                 <TextBlock TextAlignment="Center"
                                            VerticalAlignment="Center"
                                            VerticalAlignment="Center"
@@ -126,7 +126,7 @@
                         <Separator />
                         <Separator />
                         <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                         <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                             <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
                             <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
-                                          ui:Translator.TooltipKey="TOGGLE_SNAPPING"
+                                          ui:Translator.TooltipKey="TOGGLE_GRIDLINES"
                                           Classes="OverlayToggleButton pixi-icon"
                                           Classes="OverlayToggleButton pixi-icon"
                                           Content="{DynamicResource icon-gridlines}"
                                           Content="{DynamicResource icon-gridlines}"
                                           IsChecked="{Binding GridLinesVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}" />
                                           IsChecked="{Binding GridLinesVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}" />

+ 4 - 1
src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs

@@ -236,7 +236,7 @@ internal class ViewportOverlays
         {
         {
             Source = Viewport, Path = "Document.LineToolOverlayViewModel.IsSizeBoxEnabled", Mode = BindingMode.TwoWay
             Source = Viewport, Path = "Document.LineToolOverlayViewModel.IsSizeBoxEnabled", Mode = BindingMode.TwoWay
         };
         };
-
+        
         lineToolOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         lineToolOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         lineToolOverlay.Bind(LineToolOverlay.SnappingControllerProperty, snappingBinding);
         lineToolOverlay.Bind(LineToolOverlay.SnappingControllerProperty, snappingBinding);
         lineToolOverlay.Bind(LineToolOverlay.ActionCompletedProperty, actionCompletedBinding);
         lineToolOverlay.Bind(LineToolOverlay.ActionCompletedProperty, actionCompletedBinding);
@@ -244,6 +244,9 @@ internal class ViewportOverlays
         lineToolOverlay.Bind(LineToolOverlay.LineEndProperty, lineEndBinding);
         lineToolOverlay.Bind(LineToolOverlay.LineEndProperty, lineEndBinding);
         lineToolOverlay.Bind(LineToolOverlay.ShowHandlesProperty, showHandlesBinding);
         lineToolOverlay.Bind(LineToolOverlay.ShowHandlesProperty, showHandlesBinding);
         lineToolOverlay.Bind(LineToolOverlay.IsSizeBoxEnabledProperty, isSizeBoxEnabledBinding);
         lineToolOverlay.Bind(LineToolOverlay.IsSizeBoxEnabledProperty, isSizeBoxEnabledBinding);
+        lineToolOverlay.Bind(LineToolOverlay.ShowHandlesProperty, showHandlesBinding);
+        lineToolOverlay.Bind(LineToolOverlay.IsSizeBoxEnabledProperty, isSizeBoxEnabledBinding);
+        lineToolOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
     }
     }
 
 
     private void BindTransformOverlay()
     private void BindTransformOverlay()

+ 11 - 6
src/PixiEditor/Views/Overlays/LineToolOverlay/LineToolOverlay.cs

@@ -82,8 +82,6 @@ internal class LineToolOverlay : Overlay
     }
     }
 
 
 
 
-    private DashedStroke dashedStroke = new DashedStroke();
-
     private Paint blackPaint = new Paint()
     private Paint blackPaint = new Paint()
     {
     {
         Color = Colors.Black, StrokeWidth = 1, Style = PaintStyle.Stroke, IsAntiAliased = true
         Color = Colors.Black, StrokeWidth = 1, Style = PaintStyle.Stroke, IsAntiAliased = true
@@ -131,7 +129,7 @@ internal class LineToolOverlay : Overlay
         moveHandle.StrokePaint = blackPaint;
         moveHandle.StrokePaint = blackPaint;
         moveHandle.OnDrag += MoveHandleOnDrag;
         moveHandle.OnDrag += MoveHandleOnDrag;
         endHandle.Cursor = new Cursor(StandardCursorType.Arrow);
         endHandle.Cursor = new Cursor(StandardCursorType.Arrow);
-        moveHandle.OnHover += (handle, _)=> Refresh();
+        moveHandle.OnHover += (handle, _) => Refresh();
         moveHandle.OnRelease += OnHandleRelease;
         moveHandle.OnRelease += OnHandleRelease;
         AddHandle(moveHandle);
         AddHandle(moveHandle);
 
 
@@ -141,9 +139,15 @@ internal class LineToolOverlay : Overlay
     protected override void OnOverlayPointerMoved(OverlayPointerArgs args)
     protected override void OnOverlayPointerMoved(OverlayPointerArgs args)
     {
     {
         base.OnOverlayPointerMoved(args);
         base.OnOverlayPointerMoved(args);
+
         lastMousePos = args.Point;
         lastMousePos = args.Point;
     }
     }
 
 
+    public override bool TestHit(VecD point)
+    {
+        return IsVisible;
+    }
+
     private void OnHandleRelease(Handle obj, OverlayPointerArgs args)
     private void OnHandleRelease(Handle obj, OverlayPointerArgs args)
     {
     {
         if (SnappingController != null)
         if (SnappingController != null)
@@ -160,7 +164,6 @@ internal class LineToolOverlay : Overlay
     protected override void ZoomChanged(double newZoom)
     protected override void ZoomChanged(double newZoom)
     {
     {
         blackPaint.StrokeWidth = 1 / (float)newZoom;
         blackPaint.StrokeWidth = 1 / (float)newZoom;
-        dashedStroke.UpdateZoom((float)newZoom);
         infoBox.ZoomScale = newZoom;
         infoBox.ZoomScale = newZoom;
     }
     }
 
 
@@ -177,8 +180,6 @@ internal class LineToolOverlay : Overlay
 
 
         moveHandle.Position = TransformHelper.GetHandlePos(new ShapeCorners(center, size), ZoomScale, moveHandle.Size);
         moveHandle.Position = TransformHelper.GetHandlePos(new ShapeCorners(center, size), ZoomScale, moveHandle.Size);
 
 
-        dashedStroke.Draw(context, mappedStart, mappedEnd);
-
         if (ShowHandles)
         if (ShowHandles)
         {
         {
             startHandle.Draw(context);
             startHandle.Draw(context);
@@ -200,6 +201,7 @@ internal class LineToolOverlay : Overlay
 
 
         movedWhileMouseDown = false;
         movedWhileMouseDown = false;
         mouseDownPos = args.Point;
         mouseDownPos = args.Point;
+        
         lineStartOnMouseDown = LineStart;
         lineStartOnMouseDown = LineStart;
         lineEndOnMouseDown = LineEnd;
         lineEndOnMouseDown = LineEnd;
 
 
@@ -278,11 +280,14 @@ internal class LineToolOverlay : Overlay
 
 
     protected override void OnOverlayPointerReleased(OverlayPointerArgs args)
     protected override void OnOverlayPointerReleased(OverlayPointerArgs args)
     {
     {
+        IsSizeBoxEnabled = false;
+        
         if (args.InitialPressMouseButton != MouseButton.Left)
         if (args.InitialPressMouseButton != MouseButton.Left)
             return;
             return;
 
 
         if (movedWhileMouseDown && ActionCompleted is not null && ActionCompleted.CanExecute(null))
         if (movedWhileMouseDown && ActionCompleted is not null && ActionCompleted.CanExecute(null))
             ActionCompleted.Execute(null);
             ActionCompleted.Execute(null);
+        
     }
     }
 
 
     private ((string, string), VecD) TrySnapLine(VecD originalStart, VecD originalEnd, VecD delta)
     private ((string, string), VecD) TrySnapLine(VecD originalStart, VecD originalEnd, VecD delta)

+ 11 - 0
src/PixiEditor/Views/Overlays/Overlay.cs

@@ -44,6 +44,7 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
     }
     }
 
 
     public event Action? RefreshRequested;
     public event Action? RefreshRequested;
+    public event Action? RefreshCursorRequested;
     public event PointerEvent? PointerEnteredOverlay;
     public event PointerEvent? PointerEnteredOverlay;
     public event PointerEvent? PointerExitedOverlay;
     public event PointerEvent? PointerExitedOverlay;
     public event PointerEvent? PointerMovedOverlay;
     public event PointerEvent? PointerMovedOverlay;
@@ -77,6 +78,11 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
         InvalidateVisual(); // For elements in visual tree
         InvalidateVisual(); // For elements in visual tree
     }
     }
 
 
+    public void ForceRefreshCursor()
+    {
+        RefreshCursorRequested?.Invoke();
+    }
+
     public void CaptureHandle(Handle handle)
     public void CaptureHandle(Handle handle)
     {
     {
         CapturedHandle = handle;
         CapturedHandle = handle;
@@ -273,6 +279,11 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
         {
         {
             Refresh();
             Refresh();
         }
         }
+        else
+        {
+            Cursor = null;
+            RefreshCursorRequested?.Invoke();
+        }
     }
     }
 
 
     protected static void AffectsOverlayRender(params AvaloniaProperty[] properties)
     protected static void AffectsOverlayRender(params AvaloniaProperty[] properties)

+ 29 - 4
src/PixiEditor/Views/Rendering/Scene.cs

@@ -259,7 +259,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         if (checkerBitmap == null) return;
         if (checkerBitmap == null) return;
 
 
         RectD operationSurfaceRectToRender = new RectD(0, 0, dirtyBounds.Width, dirtyBounds.Height);
         RectD operationSurfaceRectToRender = new RectD(0, 0, dirtyBounds.Width, dirtyBounds.Height);
-        float checkerScale = (float)ZoomToViewportConverter.ZoomToViewport(16, Scale) * 0.25f;
+        float checkerScale = (float)ZoomToViewportConverter.ZoomToViewport(16, Scale) * 0.5f;
         checkerPaint?.Dispose();
         checkerPaint?.Dispose();
         checkerPaint = new Paint
         checkerPaint = new Paint
         {
         {
@@ -317,6 +317,8 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         {
         {
             OverlayPointerArgs args = ConstructPointerArgs(e);
             OverlayPointerArgs args = ConstructPointerArgs(e);
 
 
+            Cursor = DefaultCursor;
+            
             if (capturedOverlay != null)
             if (capturedOverlay != null)
             {
             {
                 capturedOverlay.MovePointer(args);
                 capturedOverlay.MovePointer(args);
@@ -371,11 +373,13 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             }
             }
             else
             else
             {
             {
-                for (var i = 0; i < mouseOverOverlays.Count; i++)
+                foreach (var overlay in AllOverlays)
                 {
                 {
-                    var overlay = mouseOverOverlays[i];
                     if (args.Handled) break;
                     if (args.Handled) break;
                     if (!overlay.IsVisible) continue;
                     if (!overlay.IsVisible) continue;
+                    
+                    if(!overlay.IsHitTestVisible || !overlay.TestHit(args.Point)) continue;
+                    
                     overlay.PressPointer(args);
                     overlay.PressPointer(args);
                 }
                 }
             }
             }
@@ -419,11 +423,13 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             }
             }
             else
             else
             {
             {
-                foreach (Overlay overlay in mouseOverOverlays)
+                foreach (Overlay overlay in AllOverlays)
                 {
                 {
                     if (args.Handled) break;
                     if (args.Handled) break;
                     if (!overlay.IsVisible) continue;
                     if (!overlay.IsVisible) continue;
 
 
+                    if(!overlay.IsHitTestVisible || !overlay.TestHit(args.Point)) continue;
+                    
                     overlay.ReleasePointer(args);
                     overlay.ReleasePointer(args);
                 }
                 }
             }
             }
@@ -494,6 +500,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             foreach (Overlay overlay in e.OldItems)
             foreach (Overlay overlay in e.OldItems)
             {
             {
                 overlay.RefreshRequested -= QueueRender;
                 overlay.RefreshRequested -= QueueRender;
+                overlay.RefreshCursorRequested -= RefreshCursor;
             }
             }
         }
         }
 
 
@@ -502,6 +509,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             foreach (Overlay overlay in e.NewItems)
             foreach (Overlay overlay in e.NewItems)
             {
             {
                 overlay.RefreshRequested += QueueRender;
                 overlay.RefreshRequested += QueueRender;
+                overlay.RefreshCursorRequested += RefreshCursor;
             }
             }
         }
         }
     }
     }
@@ -610,6 +618,23 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
 
     #endregion
     #endregion
 
 
+    public void RefreshCursor()
+    {
+        Cursor = DefaultCursor;
+        if (AllOverlays != null)
+        {
+            foreach (Overlay overlay in AllOverlays)
+            {
+                if (!overlay.IsVisible) continue;
+                
+                if (overlay.IsHitTestVisible)
+                {
+                    Cursor = overlay.Cursor ?? DefaultCursor;
+                }
+            }
+        }
+    }
+    
     private void QueueRender()
     private void QueueRender()
     {
     {
         Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Render);
         Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Render);

+ 1 - 1
src/PixiEditor/Views/Shortcuts/KeyCombinationBox.axaml.cs

@@ -52,7 +52,7 @@ internal partial class KeyCombinationBox : UserControl
 
 
         ViewModelMain.Current.LocalizationProvider.OnLanguageChanged += _ => UpdateText();
         ViewModelMain.Current.LocalizationProvider.OnLanguageChanged += _ => UpdateText();
 
 
-        //TOOD: Fix
+        //TODO: Fix
         //InputLanguageManager.Current.InputLanguageChanged += (_, _) => UpdateText();
         //InputLanguageManager.Current.InputLanguageChanged += (_, _) => UpdateText();
     }
     }
 
 

+ 7 - 1
src/PixiEditor/Views/Windows/Settings/SettingsWindow.axaml

@@ -128,7 +128,13 @@
                                       Name="rightClickModeComboBox"
                                       Name="rightClickModeComboBox"
                                       ItemsSource="{markupExtensions:Enum preferences:RightClickMode}"
                                       ItemsSource="{markupExtensions:Enum preferences:RightClickMode}"
                                       Width="160"
                                       Width="160"
-                                      VerticalAlignment="Center"/>
+                                      VerticalAlignment="Center">
+                                <ComboBox.ItemTemplate>
+                                    <DataTemplate>
+                                        <TextBlock ui:Translator.Key="{Binding Converter={converters:EnumToLocalizedStringConverter}}"/>
+                                    </DataTemplate>
+                                </ComboBox.ItemTemplate>
+                            </ComboBox>
                             <!--Styles="{StaticResource TranslatedEnum}"-->
                             <!--Styles="{StaticResource TranslatedEnum}"-->
                         </StackPanel>
                         </StackPanel>