Browse Source

Fixed nudge and mid change undo

flabbet 10 months ago
parent
commit
80698f751b
23 changed files with 228 additions and 90 deletions
  1. 63 20
      src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs
  2. 4 3
      src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs
  3. 6 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IExecutorFeature.cs
  4. 7 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IMidChangeUndoableExecutor.cs
  5. 13 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/ITransformableExecutor.cs
  6. 22 6
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs
  7. 38 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs
  8. 16 6
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs
  9. 16 6
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs
  10. 2 8
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs
  11. 2 2
      src/PixiEditor/Models/ExceptionHandling/CrashReport.cs
  12. 11 11
      src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs
  13. 3 1
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  14. 3 3
      src/PixiEditor/ViewModels/Document/KeyFrameViewModel.cs
  15. 1 1
      src/PixiEditor/ViewModels/Document/Nodes/ImageLayerNodeViewModel.cs
  16. 5 5
      src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs
  17. 1 1
      src/PixiEditor/ViewModels/Document/Nodes/VectorLayerNodeViewModel.cs
  18. 3 3
      src/PixiEditor/ViewModels/Document/ReferenceLayerViewModel.cs
  19. 2 2
      src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs
  20. 2 2
      src/PixiEditor/ViewModels/SubViewModels/IoViewModel.cs
  21. 1 1
      src/PixiEditor/ViewModels/SubViewModels/LayersViewModel.cs
  22. 3 1
      src/PixiEditor/ViewModels/SubViewModels/SelectionViewModel.cs
  23. 4 4
      src/PixiEditor/ViewModels/SubViewModels/UndoViewModel.cs

+ 63 - 20
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
@@ -18,7 +19,7 @@ internal class ChangeExecutionController
     public VecI LastPixelPosition => lastPixelPos;
     public VecI LastPixelPosition => lastPixelPos;
     public VecD LastPrecisePosition => lastPrecisePos;
     public VecD LastPrecisePosition => lastPrecisePos;
     public bool IsBlockingChangeActive => currentSession is not null && currentSession.BlocksOtherActions;
     public bool IsBlockingChangeActive => currentSession is not null && currentSession.BlocksOtherActions;
-    
+
     public event Action ToolSessionFinished;
     public event Action ToolSessionFinished;
 
 
     private readonly IDocument document;
     private readonly IDocument document;
@@ -29,7 +30,7 @@ internal class ChangeExecutionController
     private VecD lastPrecisePos;
     private VecD lastPrecisePos;
 
 
     private UpdateableChangeExecutor? currentSession = null;
     private UpdateableChangeExecutor? currentSession = null;
-    
+
     private UpdateableChangeExecutor? _queuedExecutor = null;
     private UpdateableChangeExecutor? _queuedExecutor = null;
 
 
     public ChangeExecutionController(IDocument document, DocumentInternalParts internals, IServiceProvider services)
     public ChangeExecutionController(IDocument document, DocumentInternalParts internals, IServiceProvider services)
@@ -39,6 +40,11 @@ internal class ChangeExecutionController
         this.services = services;
         this.services = services;
     }
     }
 
 
+    public bool IsChangeOfTypeActive<T>() where T : IExecutorFeature
+    {
+        return currentSession is T sessionT && sessionT.IsFeatureEnabled(sessionT);
+    }
+
     public ExecutorType GetCurrentExecutorType()
     public ExecutorType GetCurrentExecutorType()
     {
     {
         if (currentSession is null)
         if (currentSession is null)
@@ -53,7 +59,7 @@ internal class ChangeExecutionController
             return false;
             return false;
         if (force)
         if (force)
             currentSession?.ForceStop();
             currentSession?.ForceStop();
-        
+
         T executor = new T();
         T executor = new T();
         return TryStartExecutorInternal(executor);
         return TryStartExecutorInternal(executor);
     }
     }
@@ -64,7 +70,7 @@ internal class ChangeExecutionController
             return false;
             return false;
         if (force)
         if (force)
             currentSession?.ForceStop();
             currentSession?.ForceStop();
-        
+
         return TryStartExecutorInternal(brandNewExecutor);
         return TryStartExecutorInternal(brandNewExecutor);
     }
     }
 
 
@@ -76,7 +82,7 @@ internal class ChangeExecutionController
     private bool TryStartExecutorInternal(UpdateableChangeExecutor executor)
     private bool TryStartExecutorInternal(UpdateableChangeExecutor executor)
     {
     {
         executor.Initialize(document, internals, services, this, EndExecutor);
         executor.Initialize(document, internals, services, this, EndExecutor);
-        
+
         if (executor.StartMode == ExecutorStartMode.OnMouseLeftButtonDown)
         if (executor.StartMode == ExecutorStartMode.OnMouseLeftButtonDown)
         {
         {
             _queuedExecutor = executor;
             _queuedExecutor = executor;
@@ -85,7 +91,7 @@ internal class ChangeExecutionController
 
 
         return StartExecutor(executor);
         return StartExecutor(executor);
     }
     }
-    
+
     private bool StartExecutor(UpdateableChangeExecutor brandNewExecutor)
     private bool StartExecutor(UpdateableChangeExecutor brandNewExecutor)
     {
     {
         if (brandNewExecutor.Start() == ExecutionState.Success)
         if (brandNewExecutor.Start() == ExecutionState.Success)
@@ -103,7 +109,7 @@ internal class ChangeExecutionController
             throw new InvalidOperationException();
             throw new InvalidOperationException();
         currentSession = null;
         currentSession = null;
         _queuedExecutor = null;
         _queuedExecutor = null;
-        
+
         ToolSessionFinished?.Invoke();
         ToolSessionFinished?.Invoke();
     }
     }
 
 
@@ -113,13 +119,32 @@ internal class ChangeExecutionController
             return false;
             return false;
         currentSession.ForceStop();
         currentSession.ForceStop();
         currentSession = null;
         currentSession = null;
-        
+
         ToolSessionFinished?.Invoke();
         ToolSessionFinished?.Invoke();
         return true;
         return true;
     }
     }
 
 
-    public void MidChangeUndoInlet() => currentSession?.OnMidChangeUndo();
-    public void MidChangeRedoInlet() => currentSession?.OnMidChangeRedo();
+    public void MidChangeUndoInlet()
+    {
+        if (currentSession is null)
+            return;
+
+        if (currentSession is IMidChangeUndoableExecutor undoableExecutor)
+        {
+            undoableExecutor.OnMidChangeUndo();
+        }
+    }
+
+    public void MidChangeRedoInlet()
+    {
+        if (currentSession is null)
+            return;
+
+        if (currentSession is IMidChangeUndoableExecutor undoableExecutor)
+        {
+            undoableExecutor.OnMidChangeRedo();
+        }
+    }
 
 
     public void ConvertedKeyDownInlet(Key key)
     public void ConvertedKeyDownInlet(Key key)
     {
     {
@@ -141,6 +166,7 @@ internal class ChangeExecutionController
             lastPixelPos = newPixelPos;
             lastPixelPos = newPixelPos;
             pixelPosChanged = true;
             pixelPosChanged = true;
         }
         }
+
         lastPrecisePos = newCanvasPos;
         lastPrecisePos = newCanvasPos;
 
 
         //call session events
         //call session events
@@ -153,13 +179,16 @@ internal class ChangeExecutionController
     }
     }
 
 
     public void OpacitySliderDragStartedInlet() => currentSession?.OnOpacitySliderDragStarted();
     public void OpacitySliderDragStartedInlet() => currentSession?.OnOpacitySliderDragStarted();
+
     public void OpacitySliderDraggedInlet(float newValue)
     public void OpacitySliderDraggedInlet(float newValue)
     {
     {
         currentSession?.OnOpacitySliderDragged(newValue);
         currentSession?.OnOpacitySliderDragged(newValue);
     }
     }
+
     public void OpacitySliderDragEndedInlet() => currentSession?.OnOpacitySliderDragEnded();
     public void OpacitySliderDragEndedInlet() => currentSession?.OnOpacitySliderDragEnded();
 
 
     public void SymmetryDragStartedInlet(SymmetryAxisDirection dir) => currentSession?.OnSymmetryDragStarted(dir);
     public void SymmetryDragStartedInlet(SymmetryAxisDirection dir) => currentSession?.OnSymmetryDragStarted(dir);
+
     public void SymmetryDraggedInlet(SymmetryAxisDragInfo info)
     public void SymmetryDraggedInlet(SymmetryAxisDragInfo info)
     {
     {
         currentSession?.OnSymmetryDragged(info);
         currentSession?.OnSymmetryDragged(info);
@@ -176,7 +205,7 @@ internal class ChangeExecutionController
         {
         {
             StartExecutor(_queuedExecutor);
             StartExecutor(_queuedExecutor);
         }
         }
-        
+
         //call session event
         //call session event
         currentSession?.OnLeftMouseButtonDown(canvasPos);
         currentSession?.OnLeftMouseButtonDown(canvasPos);
     }
     }
@@ -192,17 +221,26 @@ internal class ChangeExecutionController
 
 
     public void TransformMovedInlet(ShapeCorners corners)
     public void TransformMovedInlet(ShapeCorners corners)
     {
     {
-        LastTransformState = corners;
-        currentSession?.OnTransformMoved(corners);
+        if (currentSession is ITransformableExecutor transformableExecutor)
+        {
+            LastTransformState = corners;
+            transformableExecutor.OnTransformMoved(corners);
+        }
     }
     }
-    
+
     public void MembersSelectedInlet(List<Guid> memberGuids)
     public void MembersSelectedInlet(List<Guid> memberGuids)
     {
     {
         currentSession?.OnMembersSelected(memberGuids);
         currentSession?.OnMembersSelected(memberGuids);
     }
     }
-    
-    public void TransformAppliedInlet() => currentSession?.OnTransformApplied();
-    
+
+    public void TransformAppliedInlet()
+    {
+        if (currentSession is ITransformableExecutor transformableExecutor)
+        {
+            transformableExecutor.OnTransformApplied();
+        }
+    } 
+
     public void SettingsChangedInlet(string name, object value)
     public void SettingsChangedInlet(string name, object value)
     {
     {
         currentSession?.OnSettingsChanged(name, value);
         currentSession?.OnSettingsChanged(name, value);
@@ -210,12 +248,18 @@ internal class ChangeExecutionController
 
 
     public void LineOverlayMovedInlet(VecD start, VecD end)
     public void LineOverlayMovedInlet(VecD start, VecD end)
     {
     {
-        currentSession?.OnLineOverlayMoved(start, end);
+        if (currentSession is ITransformableExecutor lineOverlayExecutor)
+        {
+            lineOverlayExecutor.OnLineOverlayMoved(start, end);
+        } 
     }
     }
 
 
     public void SelectedObjectNudgedInlet(VecI distance)
     public void SelectedObjectNudgedInlet(VecI distance)
     {
     {
-        currentSession?.OnSelectedObjectNudged(distance);
+        if (currentSession is ITransformableExecutor transformableExecutor)
+        {
+            transformableExecutor.OnSelectedObjectNudged(distance);
+        } 
     }
     }
 
 
     public void PrimaryColorChangedInlet(Color color)
     public void PrimaryColorChangedInlet(Color color)
@@ -227,5 +271,4 @@ internal class ChangeExecutionController
     {
     {
         currentSession?.OnColorChanged(color, false);
         currentSession?.OnColorChanged(color, false);
     }
     }
-
 }
 }

+ 4 - 3
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -11,6 +11,7 @@ using PixiEditor.DrawingApi.Core.Surfaces.Vector;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Models.Clipboard;
 using PixiEditor.Models.Clipboard;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
@@ -375,7 +376,7 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// </summary>
     /// </summary>
     public void Undo()
     public void Undo()
     {
     {
-        if (Internals.ChangeController.IsBlockingChangeActive)
+        if (Internals.ChangeController.IsChangeOfTypeActive<IMidChangeUndoableExecutor>())
         {
         {
             Internals.ChangeController.MidChangeUndoInlet();
             Internals.ChangeController.MidChangeUndoInlet();
             return;
             return;
@@ -389,7 +390,7 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// </summary>
     /// </summary>
     public void Redo()
     public void Redo()
     {
     {
-        if (Internals.ChangeController.IsBlockingChangeActive)
+        if (Internals.ChangeController.IsChangeOfTypeActive<IMidChangeUndoableExecutor>())
         {
         {
             Internals.ChangeController.MidChangeRedoInlet();
             Internals.ChangeController.MidChangeRedoInlet();
             return;
             return;
@@ -400,7 +401,7 @@ internal class DocumentOperationsModule : IDocumentOperations
 
 
     public void NudgeSelectedObject(VecI distance)
     public void NudgeSelectedObject(VecI distance)
     {
     {
-        if (Internals.ChangeController.IsBlockingChangeActive)
+        if (Internals.ChangeController.IsChangeOfTypeActive<ITransformableExecutor>())
         {
         {
             Internals.ChangeController.SelectedObjectNudgedInlet(distance);
             Internals.ChangeController.SelectedObjectNudgedInlet(distance);
         }
         }

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

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+
+public interface IExecutorFeature
+{
+    public bool IsFeatureEnabled(IExecutorFeature feature);
+}

+ 7 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IMidChangeUndoableExecutor.cs

@@ -0,0 +1,7 @@
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+
+public interface IMidChangeUndoableExecutor : IExecutorFeature
+{
+    public void OnMidChangeUndo();
+    public void OnMidChangeRedo();
+}

+ 13 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/ITransformableExecutor.cs

@@ -0,0 +1,13 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+
+public interface ITransformableExecutor : IExecutorFeature
+{
+    public bool IsTransforming { get; }
+    public void OnTransformMoved(ShapeCorners corners); 
+    public void OnTransformApplied();
+    public void OnLineOverlayMoved(VecD start, VecD end);
+    public void OnSelectedObjectNudged(VecI distance);
+}

+ 22 - 6
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs

@@ -4,13 +4,14 @@ using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces.Vector;
 using PixiEditor.DrawingApi.Core.Surfaces.Vector;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
 #nullable enable
-internal class PasteImageExecutor : UpdateableChangeExecutor
+internal class PasteImageExecutor : UpdateableChangeExecutor, ITransformableExecutor, IMidChangeUndoableExecutor
 {
 {
     private readonly Surface image;
     private readonly Surface image;
     private readonly VecI pos;
     private readonly VecI pos;
@@ -60,18 +61,22 @@ internal class PasteImageExecutor : UpdateableChangeExecutor
         return ExecutionState.Success;
         return ExecutionState.Success;
     }
     }
 
 
-    public override void OnTransformMoved(ShapeCorners corners)
+    public bool IsTransforming => true; 
+
+    public void OnTransformMoved(ShapeCorners corners)
     {
     {
         internals!.ActionAccumulator.AddActions(new PasteImage_Action(image, corners, memberGuid.Value, false, drawOnMask, document!.AnimationHandler.ActiveFrameBindable, default));
         internals!.ActionAccumulator.AddActions(new PasteImage_Action(image, corners, memberGuid.Value, false, drawOnMask, document!.AnimationHandler.ActiveFrameBindable, default));
     }
     }
 
 
-    public override void OnSelectedObjectNudged(VecI distance) => document!.TransformHandler.Nudge(distance);
+    public void OnLineOverlayMoved(VecD start, VecD end) { }
+
+    public void OnSelectedObjectNudged(VecI distance) => document!.TransformHandler.Nudge(distance);
 
 
-    public override void OnMidChangeUndo() => document!.TransformHandler.Undo();
+    public void OnMidChangeUndo() => document!.TransformHandler.Undo();
 
 
-    public override void OnMidChangeRedo() => document!.TransformHandler.Redo();
+    public void OnMidChangeRedo() => document!.TransformHandler.Redo();
 
 
-    public override void OnTransformApplied()
+    public void OnTransformApplied()
     {
     {
         internals!.ActionAccumulator.AddFinishedActions(new EndPasteImage_Action());
         internals!.ActionAccumulator.AddFinishedActions(new EndPasteImage_Action());
         document!.TransformHandler.HideTransform();
         document!.TransformHandler.HideTransform();
@@ -83,4 +88,15 @@ internal class PasteImageExecutor : UpdateableChangeExecutor
         document!.TransformHandler.HideTransform();
         document!.TransformHandler.HideTransform();
         internals!.ActionAccumulator.AddActions(new EndPasteImage_Action());
         internals!.ActionAccumulator.AddActions(new EndPasteImage_Action());
     }
     }
+
+    public bool IsFeatureEnabled(IExecutorFeature feature)
+    {
+        if (feature is ITransformableExecutor)
+            return IsTransforming;
+        
+        if (feature is IMidChangeUndoableExecutor)
+            return true;
+
+        return false;
+    }
 }
 }

+ 38 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs

@@ -1,4 +1,6 @@
-using PixiEditor.Models.Handlers;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 
 
@@ -22,7 +24,8 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 ///         - Transform -> Preview (when user applies the transform)
 ///         - Transform -> Preview (when user applies the transform)
 ///         - 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
 {
 {
     private ShapeToolMode activeMode;
     private ShapeToolMode activeMode;
 
 
@@ -134,13 +137,28 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor
         ActiveMode = ShapeToolMode.Transform;
         ActiveMode = ShapeToolMode.Transform;
     }
     }
 
 
-    public override void OnTransformApplied()
+    public bool IsTransforming => ActiveMode == ShapeToolMode.Transform; 
+
+    public virtual void OnTransformMoved(ShapeCorners corners)
+    {
+        
+    }
+
+    public virtual void OnTransformApplied()
     {
     {
         ActiveMode = ShapeToolMode.Preview;
         ActiveMode = ShapeToolMode.Preview;
         AddMemberToSnapping();
         AddMemberToSnapping();
         HighlightSnapping(null, null);
         HighlightSnapping(null, null);
     }
     }
-    
+
+    public virtual void OnLineOverlayMoved(VecD start, VecD end)
+    {
+    }
+
+    public virtual void OnSelectedObjectNudged(VecI distance)
+    {
+    }
+
     public override void ForceStop()
     public override void ForceStop()
     {
     {
         StopMode(activeMode);
         StopMode(activeMode);
@@ -187,6 +205,22 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor
     
     
     protected virtual void PrecisePositionChangeDrawingMode(VecD pos) { }
     protected virtual void PrecisePositionChangeDrawingMode(VecD pos) { }
     protected virtual void PrecisePositionChangeTransformMode(VecD pos) { }
     protected virtual void PrecisePositionChangeTransformMode(VecD pos) { }
+    public abstract void OnMidChangeUndo();
+    public abstract void OnMidChangeRedo();
+    public bool IsFeatureEnabled(IExecutorFeature feature)
+    {
+        if (feature is ITransformableExecutor)
+        {
+            return IsTransforming;
+        }
+        
+        if (feature is IMidChangeUndoableExecutor)
+        {
+            return ActiveMode == ShapeToolMode.Transform;
+        }
+        
+        return false;
+    }
 }
 }
 
 
 enum ShapeToolMode
 enum ShapeToolMode

+ 16 - 6
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs

@@ -1,11 +1,12 @@
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
-internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor
+internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor, ITransformableExecutor
 {
 {
     public override ExecutionState Start()
     public override ExecutionState Start()
     {
     {
@@ -19,18 +20,22 @@ internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor
         return ExecutionState.Success;
         return ExecutionState.Success;
     }
     }
 
 
-    public override void OnTransformMoved(ShapeCorners corners)
+    public bool IsTransforming => true;
+
+    public void OnTransformMoved(ShapeCorners corners)
     {
     {
         internals!.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(corners));
         internals!.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(corners));
     }
     }
 
 
-    public override void OnSelectedObjectNudged(VecI distance) => document!.TransformHandler.Nudge(distance);
+    public void OnLineOverlayMoved(VecD start, VecD end) { }
+
+    public void OnSelectedObjectNudged(VecI distance) => document!.TransformHandler.Nudge(distance);
 
 
-    public override void OnMidChangeUndo() => document!.TransformHandler.Undo();
+    public void OnMidChangeUndo() => document!.TransformHandler.Undo();
 
 
-    public override void OnMidChangeRedo() => document!.TransformHandler.Redo();
+    public void OnMidChangeRedo() => document!.TransformHandler.Redo();
 
 
-    public override void OnTransformApplied()
+    public void OnTransformApplied()
     {
     {
         internals!.ActionAccumulator.AddFinishedActions(new EndTransformReferenceLayer_Action());
         internals!.ActionAccumulator.AddFinishedActions(new EndTransformReferenceLayer_Action());
         document!.TransformHandler.HideTransform();
         document!.TransformHandler.HideTransform();
@@ -44,4 +49,9 @@ internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor
         document!.TransformHandler.HideTransform();
         document!.TransformHandler.HideTransform();
         document.ReferenceLayerHandler.IsTransforming = false;
         document.ReferenceLayerHandler.IsTransforming = false;
     }
     }
+
+    public bool IsFeatureEnabled(IExecutorFeature feature)
+    {
+        return feature is ITransformableExecutor;
+    }
 }
 }

+ 16 - 6
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs

@@ -5,6 +5,7 @@ using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces.Vector;
 using PixiEditor.DrawingApi.Core.Surfaces.Vector;
 using PixiEditor.Models.DocumentModels.Public;
 using PixiEditor.Models.DocumentModels.Public;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
@@ -13,7 +14,7 @@ using PixiEditor.ViewModels.Document.Nodes;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
 #nullable enable
-internal class TransformSelectedExecutor : UpdateableChangeExecutor
+internal class TransformSelectedExecutor : UpdateableChangeExecutor, ITransformableExecutor, IMidChangeUndoableExecutor
 {
 {
     private Dictionary<Guid, ShapeCorners> memberCorners = new();
     private Dictionary<Guid, ShapeCorners> memberCorners = new();
     private IMoveToolHandler? tool;
     private IMoveToolHandler? tool;
@@ -111,7 +112,9 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor
         }));
         }));
     }
     }
 
 
-    public override void OnTransformMoved(ShapeCorners corners)
+    public bool IsTransforming => isInProgress;
+
+    public void OnTransformMoved(ShapeCorners corners)
     {
     {
         if (!isInProgress)
         if (!isInProgress)
             return;
             return;
@@ -121,13 +124,15 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor
                 document!.AnimationHandler.ActiveFrameBindable));
                 document!.AnimationHandler.ActiveFrameBindable));
     }
     }
 
 
-    public override void OnSelectedObjectNudged(VecI distance) => document!.TransformHandler.Nudge(distance);
+    public void OnLineOverlayMoved(VecD start, VecD end) { }
+
+    public void OnSelectedObjectNudged(VecI distance) => document!.TransformHandler.Nudge(distance);
 
 
-    public override void OnMidChangeUndo() => document!.TransformHandler.Undo();
+    public void OnMidChangeUndo() => document!.TransformHandler.Undo();
 
 
-    public override void OnMidChangeRedo() => document!.TransformHandler.Redo();
+    public void OnMidChangeRedo() => document!.TransformHandler.Redo();
 
 
-    public override void OnTransformApplied()
+    public void OnTransformApplied()
     {
     {
         if (tool is not null)
         if (tool is not null)
         {
         {
@@ -179,4 +184,9 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor
             }
             }
         }
         }
     }
     }
+
+    public bool IsFeatureEnabled(IExecutorFeature feature)
+    {
+        return feature is ITransformableExecutor && IsTransforming;
+    }
 }
 }

+ 2 - 8
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs

@@ -22,7 +22,8 @@ internal abstract class UpdateableChangeExecutor
     protected Action<UpdateableChangeExecutor>? onEnded;
     protected Action<UpdateableChangeExecutor>? onEnded;
     public virtual ExecutorType Type => ExecutorType.Regular;
     public virtual ExecutorType Type => ExecutorType.Regular;
     public virtual ExecutorStartMode StartMode => ExecutorStartMode.RightAway;
     public virtual ExecutorStartMode StartMode => ExecutorStartMode.RightAway;
-    public virtual bool BlocksOtherActions => true; 
+    public virtual bool BlocksOtherActions => true;
+    public virtual bool IsUndoable => true;
 
 
     public void Initialize(IDocument document, DocumentInternalParts internals, IServiceProvider services,
     public void Initialize(IDocument document, DocumentInternalParts internals, IServiceProvider services,
         ChangeExecutionController controller, Action<UpdateableChangeExecutor> onEnded)
         ChangeExecutionController controller, Action<UpdateableChangeExecutor> onEnded)
@@ -58,13 +59,6 @@ internal abstract class UpdateableChangeExecutor
     public virtual void OnSymmetryDragEnded(SymmetryAxisDirection dir) { }
     public virtual void OnSymmetryDragEnded(SymmetryAxisDirection dir) { }
     public virtual void OnConvertedKeyDown(Key key) { }
     public virtual void OnConvertedKeyDown(Key key) { }
     public virtual void OnConvertedKeyUp(Key key) { }
     public virtual void OnConvertedKeyUp(Key key) { }
-    public virtual void OnTransformMoved(ShapeCorners corners) { }
-    public virtual void OnTransformApplied() { }
-    public virtual void OnLineOverlayMoved(VecD start, VecD end) { }
-    public virtual void OnMidChangeUndo() { }
-    public virtual void OnMidChangeRedo() { }
-    public virtual void OnSelectedObjectNudged(VecI distance) { }
-
     public virtual void OnSettingsChanged(string name, object value) { }
     public virtual void OnSettingsChanged(string name, object value) { }
     public virtual void OnColorChanged(Color color, bool primary) { }
     public virtual void OnColorChanged(Color color, bool primary) { }
     public virtual void OnMembersSelected(List<Guid> memberGuids) { }
     public virtual void OnMembersSelected(List<Guid> memberGuids) { }

+ 2 - 2
src/PixiEditor/Models/ExceptionHandling/CrashReport.cs

@@ -129,7 +129,7 @@ internal class CrashReport : IDisposable
             .AppendLine($"  MainWindow Size: {GetFormatted(() => MainWindow.Current?.Bounds)}")
             .AppendLine($"  MainWindow Size: {GetFormatted(() => MainWindow.Current?.Bounds)}")
             .AppendLine($"  MainWindow State: {GetFormatted(() => MainWindow.Current?.WindowState)}")
             .AppendLine($"  MainWindow State: {GetFormatted(() => MainWindow.Current?.WindowState)}")
             .AppendLine("\nViewModels:")
             .AppendLine("\nViewModels:")
-            .AppendLine($"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.UpdateableChangeActive)}")
+            .AppendLine($"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive)}")
             .AppendLine($"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
             .AppendLine($"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
             .AppendLine($"  Primary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.PrimaryColor)}")
             .AppendLine($"  Primary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.PrimaryColor)}")
             .AppendLine($"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
             .AppendLine($"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
@@ -180,7 +180,7 @@ internal class CrashReport : IDisposable
             .AppendLine($"  Horizontal Symmetry Value: {FormatObject(document.HorizontalSymmetryAxisYBindable)}")
             .AppendLine($"  Horizontal Symmetry Value: {FormatObject(document.HorizontalSymmetryAxisYBindable)}")
             .AppendLine($"  Vertical Symmetry Enabled: {document.VerticalSymmetryAxisEnabledBindable}")
             .AppendLine($"  Vertical Symmetry Enabled: {document.VerticalSymmetryAxisEnabledBindable}")
             .AppendLine($"  Vertical Symmetry Value: {FormatObject(document.VerticalSymmetryAxisXBindable)}")
             .AppendLine($"  Vertical Symmetry Value: {FormatObject(document.VerticalSymmetryAxisXBindable)}")
-            .AppendLine($"  Updateable Change Active: {FormatObject(document.UpdateableChangeActive)}")
+            .AppendLine($"  Updateable Change Active: {FormatObject(document.BlockingUpdateableChangeActive)}")
             .AppendLine($"  Transform: {FormatObject(document.TransformViewModel)}");
             .AppendLine($"  Transform: {FormatObject(document.TransformViewModel)}");
     }
     }
 
 

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

@@ -34,7 +34,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         get => _activeFrameBindable;
         get => _activeFrameBindable;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
 
 
             Internals.ActionAccumulator.AddActions(new SetActiveFrame_PassthroughAction(value));
             Internals.ActionAccumulator.AddActions(new SetActiveFrame_PassthroughAction(value));
@@ -48,7 +48,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         get => frameRateBindable;
         get => frameRateBindable;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
 
 
             Internals.ActionAccumulator.AddFinishedActions(new SetFrameRate_Action(value));
             Internals.ActionAccumulator.AddFinishedActions(new SetFrameRate_Action(value));
@@ -60,7 +60,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         get => onionSkinningEnabled;
         get => onionSkinningEnabled;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
 
 
             Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
             Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
@@ -72,7 +72,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         get => onionFrames;
         get => onionFrames;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
 
 
             Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(value, OnionOpacityBindable));
             Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(value, OnionOpacityBindable));
@@ -84,7 +84,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         get => onionOpacity;
         get => onionOpacity;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
 
 
             Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(OnionFramesBindable, value)); 
             Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(OnionFramesBindable, value)); 
@@ -96,7 +96,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         get => isPlayingBindable;
         get => isPlayingBindable;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
 
 
             Internals.ActionAccumulator.AddFinishedActions(new SetPlayingState_PassthroughAction(value));
             Internals.ActionAccumulator.AddFinishedActions(new SetPlayingState_PassthroughAction(value));
@@ -126,7 +126,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
     public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null,
     public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null,
         int? frameToCopyFrom = null)
         int? frameToCopyFrom = null)
     {
     {
-        if (!Document.UpdateableChangeActive)
+        if (!Document.BlockingUpdateableChangeActive)
         {
         {
             Internals.ActionAccumulator.AddFinishedActions(new CreateRasterKeyFrame_Action(targetLayerGuid,
             Internals.ActionAccumulator.AddFinishedActions(new CreateRasterKeyFrame_Action(targetLayerGuid,
                 Guid.NewGuid(), Math.Max(1, frame),
                 Guid.NewGuid(), Math.Max(1, frame),
@@ -136,7 +136,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void DeleteKeyFrames(List<Guid> keyFrameIds)
     public void DeleteKeyFrames(List<Guid> keyFrameIds)
     {
     {
-        if (!Document.UpdateableChangeActive)
+        if (!Document.BlockingUpdateableChangeActive)
         {
         {
             for (var i = 0; i < keyFrameIds.Count; i++)
             for (var i = 0; i < keyFrameIds.Count; i++)
             {
             {
@@ -155,7 +155,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void ChangeKeyFramesStartPos(Guid[] infoIds, int infoDelta)
     public void ChangeKeyFramesStartPos(Guid[] infoIds, int infoDelta)
     {
     {
-        if (!Document.UpdateableChangeActive)
+        if (!Document.BlockingUpdateableChangeActive)
         {
         {
             Internals.ActionAccumulator.AddActions(new KeyFramesStartPos_Action(infoIds.ToList(), infoDelta));
             Internals.ActionAccumulator.AddActions(new KeyFramesStartPos_Action(infoIds.ToList(), infoDelta));
         }
         }
@@ -163,7 +163,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void ToggleOnionSkinning(bool value)
     public void ToggleOnionSkinning(bool value)
     {
     {
-        if (!Document.UpdateableChangeActive)
+        if (!Document.BlockingUpdateableChangeActive)
         {
         {
             Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
             Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
         }
         }
@@ -171,7 +171,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
 
     public void EndKeyFramesStartPos()
     public void EndKeyFramesStartPos()
     {
     {
-        if (!Document.UpdateableChangeActive)
+        if (!Document.BlockingUpdateableChangeActive)
         {
         {
             Internals.ActionAccumulator.AddFinishedActions(new EndKeyFramesStartPos_Action());
             Internals.ActionAccumulator.AddFinishedActions(new EndKeyFramesStartPos_Action());
         }
         }

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

@@ -39,6 +39,7 @@ using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels.Public;
 using PixiEditor.Models.DocumentModels.Public;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Serialization;
 using PixiEditor.Models.Serialization;
@@ -152,7 +153,8 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
 
     private readonly HashSet<IStructureMemberHandler> softSelectedStructureMembers = new();
     private readonly HashSet<IStructureMemberHandler> softSelectedStructureMembers = new();
 
 
-    public bool UpdateableChangeActive => Internals.ChangeController.IsBlockingChangeActive;
+    public bool BlockingUpdateableChangeActive => Internals.ChangeController.IsBlockingChangeActive;
+    public bool IsChangeFeatureActive<T>() where T : IExecutorFeature => Internals.ChangeController.IsChangeOfTypeActive<T>();
 
 
     public bool PointerDragChangeInProgress =>
     public bool PointerDragChangeInProgress =>
         Internals.ChangeController.IsBlockingChangeActive && Internals.ChangeController.LeftMousePressed;
         Internals.ChangeController.IsBlockingChangeActive && Internals.ChangeController.LeftMousePressed;

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

@@ -43,7 +43,7 @@ internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
                 value = 0;
                 value = 0;
             }
             }
 
 
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
             {
             {
                 Internals.ActionAccumulator.AddFinishedActions(
                 Internals.ActionAccumulator.AddFinishedActions(
                     new KeyFrameLength_Action(Id, value, DurationBindable),
                     new KeyFrameLength_Action(Id, value, DurationBindable),
@@ -62,7 +62,7 @@ internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
                 value = 1;
                 value = 1;
             }
             }
 
 
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
             {
             {
                 Internals.ActionAccumulator.AddFinishedActions(
                 Internals.ActionAccumulator.AddFinishedActions(
                     new KeyFrameLength_Action(Id, StartFrameBindable, value),
                     new KeyFrameLength_Action(Id, StartFrameBindable, value),
@@ -76,7 +76,7 @@ internal abstract class KeyFrameViewModel : ObservableObject, IKeyFrameHandler
         get => isVisibleBindable;
         get => isVisibleBindable;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
             {
             {
                 Internals.ActionAccumulator.AddFinishedActions(new KeyFrameVisibility_Action(Id, value));
                 Internals.ActionAccumulator.AddFinishedActions(new KeyFrameVisibility_Action(Id, value));
             }
             }

+ 1 - 1
src/PixiEditor/ViewModels/Document/Nodes/ImageLayerNodeViewModel.cs

@@ -19,7 +19,7 @@ internal class ImageLayerNodeViewModel : StructureMemberViewModel<ImageLayerNode
         get => lockTransparency;
         get => lockTransparency;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
                 Internals.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(Id, value));
                 Internals.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(Id, value));
         }
         }
     }
     }

+ 5 - 5
src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs

@@ -33,7 +33,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         get => isVisible;
         get => isVisible;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
                 Internals.ActionAccumulator.AddFinishedActions(new StructureMemberIsVisible_Action(value, Id));
                 Internals.ActionAccumulator.AddFinishedActions(new StructureMemberIsVisible_Action(value, Id));
         }
         }
     }
     }
@@ -57,7 +57,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         get => maskIsVisible;
         get => maskIsVisible;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
                 Internals.ActionAccumulator.AddFinishedActions(
                 Internals.ActionAccumulator.AddFinishedActions(
                     new StructureMemberMaskIsVisible_Action(value, Id));
                     new StructureMemberMaskIsVisible_Action(value, Id));
         }
         }
@@ -76,7 +76,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         get => blendMode;
         get => blendMode;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
                 Internals.ActionAccumulator.AddFinishedActions(new StructureMemberBlendMode_Action(value, Id));
                 Internals.ActionAccumulator.AddFinishedActions(new StructureMemberBlendMode_Action(value, Id));
         }
         }
     }
     }
@@ -94,7 +94,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         get => clipToMemberBelowEnabled;
         get => clipToMemberBelowEnabled;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
                 Internals.ActionAccumulator.AddFinishedActions(
                 Internals.ActionAccumulator.AddFinishedActions(
                     new StructureMemberClipToMemberBelow_Action(value, Id));
                     new StructureMemberClipToMemberBelow_Action(value, Id));
         }
         }
@@ -126,7 +126,7 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
         get => opacity;
         get => opacity;
         set
         set
         {
         {
-            if (Document.UpdateableChangeActive)
+            if (Document.BlockingUpdateableChangeActive)
                 return;
                 return;
             float newValue = Math.Clamp(value, 0, 1);
             float newValue = Math.Clamp(value, 0, 1);
             Internals.ActionAccumulator.AddFinishedActions(
             Internals.ActionAccumulator.AddFinishedActions(

+ 1 - 1
src/PixiEditor/ViewModels/Document/Nodes/VectorLayerNodeViewModel.cs

@@ -21,7 +21,7 @@ internal class VectorLayerNodeViewModel : StructureMemberViewModel<VectorLayerNo
         get => lockTransparency;
         get => lockTransparency;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
                 Internals.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(Id, value));
                 Internals.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(Id, value));
         }
         }
     }
     }

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

@@ -34,7 +34,7 @@ internal class ReferenceLayerViewModel : ObservableObject, IReferenceLayerHandle
         get => referenceShape; 
         get => referenceShape; 
         set
         set
         {
         {
-            if (!doc.UpdateableChangeActive)
+            if (!doc.BlockingUpdateableChangeActive)
                 internals.ActionAccumulator.AddFinishedActions(new TransformReferenceLayer_Action(value));
                 internals.ActionAccumulator.AddFinishedActions(new TransformReferenceLayer_Action(value));
         }
         }
     }
     }
@@ -57,7 +57,7 @@ internal class ReferenceLayerViewModel : ObservableObject, IReferenceLayerHandle
         get => isVisible;
         get => isVisible;
         set
         set
         {
         {
-            if (!doc.UpdateableChangeActive)
+            if (!doc.BlockingUpdateableChangeActive)
                 internals.ActionAccumulator.AddFinishedActions(new ReferenceLayerIsVisible_Action(value));
                 internals.ActionAccumulator.AddFinishedActions(new ReferenceLayerIsVisible_Action(value));
         }
         }
     }
     }
@@ -80,7 +80,7 @@ internal class ReferenceLayerViewModel : ObservableObject, IReferenceLayerHandle
         get => isTopMost;
         get => isTopMost;
         set
         set
         {
         {
-            if (!doc.UpdateableChangeActive)
+            if (!doc.BlockingUpdateableChangeActive)
                 internals.ActionAccumulator.AddFinishedActions(new ReferenceLayerTopMost_Action(value));
                 internals.ActionAccumulator.AddFinishedActions(new ReferenceLayerTopMost_Action(value));
         }
         }
     }
     }

+ 2 - 2
src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs

@@ -53,7 +53,7 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
         get => nodeNameBindable ?? DisplayName;
         get => nodeNameBindable ?? DisplayName;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
             {
             {
                 Internals.ActionAccumulator.AddFinishedActions(
                 Internals.ActionAccumulator.AddFinishedActions(
                     new SetNodeName_Action(Id, value));
                     new SetNodeName_Action(Id, value));
@@ -89,7 +89,7 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
         get => position;
         get => position;
         set
         set
         {
         {
-            if (!Document.UpdateableChangeActive)
+            if (!Document.BlockingUpdateableChangeActive)
             {
             {
                 Internals.ActionAccumulator.AddFinishedActions(
                 Internals.ActionAccumulator.AddFinishedActions(
                     new NodePosition_Action(Id, value),
                     new NodePosition_Action(Id, value),

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/IoViewModel.cs

@@ -209,7 +209,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         {
         {
             case RightClickMode.SecondaryColor when tools.ActiveTool.UsesColor:
             case RightClickMode.SecondaryColor when tools.ActiveTool.UsesColor:
             case RightClickMode.Erase when tools.ActiveTool is ColorPickerToolViewModel:
             case RightClickMode.Erase when tools.ActiveTool is ColorPickerToolViewModel:
-                if (!Owner.DocumentManagerSubViewModel.ActiveDocument.UpdateableChangeActive)
+                if (!Owner.DocumentManagerSubViewModel.ActiveDocument.BlockingUpdateableChangeActive)
                 {
                 {
                     Owner.ColorsSubViewModel.SwapColors(null);
                     Owner.ColorsSubViewModel.SwapColors(null);
                 }
                 }
@@ -310,7 +310,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
                                          }
                                          }
                                         ):
                                         ):
 
 
-                if (!Owner.DocumentManagerSubViewModel.ActiveDocument.UpdateableChangeActive)
+                if (!Owner.DocumentManagerSubViewModel.ActiveDocument.BlockingUpdateableChangeActive)
                 {
                 {
                     Owner.ColorsSubViewModel.SwapColors(null);
                     Owner.ColorsSubViewModel.SwapColors(null);
                 }
                 }

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/LayersViewModel.cs

@@ -137,7 +137,7 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
     [Evaluator.CanExecute("PixiEditor.Layer.CanCreateNewMember")]
     [Evaluator.CanExecute("PixiEditor.Layer.CanCreateNewMember")]
     public bool CanCreateNewMember()
     public bool CanCreateNewMember()
     {
     {
-        return Owner.DocumentManagerSubViewModel.ActiveDocument is { UpdateableChangeActive: false };
+        return Owner.DocumentManagerSubViewModel.ActiveDocument is { BlockingUpdateableChangeActive: false };
     }
     }
 
 
     [Command.Internal("PixiEditor.Layer.ToggleLockTransparency", CanExecute = "PixiEditor.Layer.SelectedMemberIsLayer",
     [Command.Internal("PixiEditor.Layer.ToggleLockTransparency", CanExecute = "PixiEditor.Layer.SelectedMemberIsLayer",

+ 3 - 1
src/PixiEditor/ViewModels/SubViewModels/SelectionViewModel.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
 using PixiEditor.Models.Commands.Attributes.Evaluators;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 using PixiEditor.UI.Common.Fonts;
 using PixiEditor.UI.Common.Fonts;
 
 
@@ -104,5 +105,6 @@ internal class SelectionViewModel : SubViewModel<ViewModelMain>
     }
     }
 
 
     [Evaluator.CanExecute("PixiEditor.Selection.CanNudgeSelectedObject")]
     [Evaluator.CanExecute("PixiEditor.Selection.CanNudgeSelectedObject")]
-    public bool CanNudgeSelectedObject(int[] dist) => Owner.DocumentManagerSubViewModel.ActiveDocument?.UpdateableChangeActive == true;
+    public bool CanNudgeSelectedObject(int[] dist) => Owner.DocumentManagerSubViewModel.ActiveDocument
+        ?.IsChangeFeatureActive<ITransformableExecutor>() ?? false;
 }
 }

+ 4 - 4
src/PixiEditor/ViewModels/SubViewModels/UndoViewModel.cs

@@ -21,7 +21,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     public void Redo()
     public void Redo()
     {
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
-        if (doc is null || (!doc.UpdateableChangeActive && !doc.HasSavedRedo))
+        if (doc is null || (!doc.BlockingUpdateableChangeActive && !doc.HasSavedRedo))
             return;
             return;
         doc.Operations.Redo();
         doc.Operations.Redo();
     }
     }
@@ -34,7 +34,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
     public void Undo()
     public void Undo()
     {
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
-        if (doc is null || (!doc.UpdateableChangeActive && !doc.HasSavedUndo))
+        if (doc is null || (!doc.BlockingUpdateableChangeActive && !doc.HasSavedUndo))
             return;
             return;
         doc.Operations.Undo();
         doc.Operations.Undo();
     }
     }
@@ -50,7 +50,7 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (doc is null)
         if (doc is null)
             return false;
             return false;
-        return doc.UpdateableChangeActive || doc.HasSavedUndo;
+        return doc.BlockingUpdateableChangeActive || doc.HasSavedUndo;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -64,6 +64,6 @@ internal class UndoViewModel : SubViewModel<ViewModelMain>
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (doc is null)
         if (doc is null)
             return false;
             return false;
-        return doc.UpdateableChangeActive || doc.HasSavedRedo;
+        return doc.BlockingUpdateableChangeActive || doc.HasSavedRedo;
     }
     }
 }
 }