Browse Source

Fix DocumentUpdater not having enough info when doing state synchronization

Equbuxu 3 years ago
parent
commit
5b435121de
55 changed files with 515 additions and 346 deletions
  1. 2 2
      src/ChunkyImageLib/Operations/BresenhamLineOperation.cs
  2. 1 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/LayerImageChunks_ChangeInfo.cs
  3. 2 8
      src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/MaskChunks_ChangeInfo.cs
  4. 4 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/Selection_ChangeInfo.cs
  5. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/LayerLockTransparency_ChangeInfo.cs
  6. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberBlendMode_ChangeInfo.cs
  7. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberClipToMemberBelow_ChangeInfo.cs
  8. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberIsVisible_ChangeInfo.cs
  9. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberMaskIsVisible_ChangeInfo.cs
  10. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberMask_ChangeInfo.cs
  11. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberName_ChangeInfo.cs
  12. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberOpacity_ChangeInfo.cs
  13. 1 3
      src/PixiEditor.ChangeableDocument/ChangeInfos/Root/Size_ChangeInfo.cs
  14. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Root/SymmetryAxisPosition_ChangeInfo.cs
  15. 1 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Root/SymmetryAxisState_ChangeInfo.cs
  16. 53 0
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateFolder_ChangeInfo.cs
  17. 40 0
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateLayer_ChangeInfo.cs
  18. 15 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateStructureMember_ChangeInfo.cs
  19. 1 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/DeleteStructureMember_ChangeInfo.cs
  20. 1 6
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/MoveStructureMember_ChangeInfo.cs
  21. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Folder.cs
  22. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Layer.cs
  23. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs
  24. 5 5
      src/PixiEditor.ChangeableDocument/Changes/Drawing/ApplyLayerMask_Change.cs
  25. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs
  26. 3 12
      src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs
  27. 2 10
      src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawingChangeHelper.cs
  28. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Drawing/ShiftLayer_UpdateableChange.cs
  29. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Properties/CreateStructureMemberMask_Change.cs
  30. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Properties/DeleteStructureMemberMask_Change.cs
  31. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Properties/LayerLockTransparency_Change.cs
  32. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberBlendMode_Change.cs
  33. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberClipToMemberBelow_Change.cs
  34. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberIsVisible_Change.cs
  35. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberMaskIsVisible_Change.cs
  36. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberName_Change.cs
  37. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberOpacity_UpdateableChange.cs
  38. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Root/ResizeCanvas_Change.cs
  39. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Root/SymmetryAxisPosition_UpdateableChange.cs
  40. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Root/SymmetryAxisState_Change.cs
  41. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Selection/SelectLasso_UpdateableChange.cs
  42. 2 3
      src/PixiEditor.ChangeableDocument/Changes/Selection/SelectRectangle_UpdateableChange.cs
  43. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Selection/TransformSelectionPath_UpdateableChange.cs
  44. 8 3
      src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs
  45. 9 3
      src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs
  46. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  47. 9 3
      src/PixiEditorPrototype/Models/DocumentStructureHelper.cs
  48. 58 41
      src/PixiEditorPrototype/Models/DocumentUpdater.cs
  49. 2 2
      src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs
  50. 5 5
      src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml
  51. 119 72
      src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs
  52. 3 7
      src/PixiEditorPrototype/ViewModels/FolderViewModel.cs
  53. 11 5
      src/PixiEditorPrototype/ViewModels/LayerViewModel.cs
  54. 79 33
      src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs
  55. 24 24
      src/PixiEditorPrototype/Views/MainWindow.xaml

+ 2 - 2
src/ChunkyImageLib/Operations/BresenhamLineOperation.cs

@@ -41,12 +41,12 @@ internal class BresenhamLineOperation : IDrawOperation
         if (verAxisX is not null)
         {
             newFrom = newFrom.ReflectX((int)verAxisX);
-            newTo = newFrom.ReflectX((int)verAxisX);
+            newTo = newTo.ReflectX((int)verAxisX);
         }
         if (horAxisY is not null)
         {
             newFrom = newFrom.ReflectY((int)horAxisY);
-            newTo = newFrom.ReflectY((int)horAxisY);
+            newTo = newTo.ReflectY((int)horAxisY);
         }
         return new BresenhamLineOperation(newFrom, newTo, paint.Color);
     }

+ 1 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/LayerImageChunks_ChangeInfo.cs

@@ -1,7 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
 
-public record class LayerImageChunks_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-    public HashSet<VecI>? Chunks { get; init; }
-}
+public record class LayerImageChunks_ChangeInfo(Guid GuidValue, HashSet<VecI> Chunks) : IChangeInfo;

+ 2 - 8
src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/MaskChunks_ChangeInfo.cs

@@ -1,9 +1,3 @@
-using ChunkyImageLib.DataHolders;
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
 
-namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
-
-public record class MaskChunks_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-    public HashSet<VecI>? Chunks { get; init; }
-}
+public record class MaskChunks_ChangeInfo(Guid GuidValue, HashSet<VecI> Chunks) : IChangeInfo;

+ 4 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/Selection_ChangeInfo.cs

@@ -1,5 +1,5 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
+using SkiaSharp;
 
-public record class Selection_ChangeInfo : IChangeInfo
-{
-}
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
+
+public record class Selection_ChangeInfo(SKPath NewPath) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/LayerLockTransparency_ChangeInfo.cs

@@ -1,5 +1,2 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
-public record class LayerLockTransparency_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class LayerLockTransparency_ChangeInfo(Guid GuidValue, bool LockTransparency) : IChangeInfo;

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberBlendMode_ChangeInfo.cs

@@ -1,5 +1,4 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
-public record class StructureMemberBlendMode_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+using PixiEditor.ChangeableDocument.Enums;
+
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
+public record class StructureMemberBlendMode_ChangeInfo(Guid GuidValue, BlendMode BlendMode) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberClipToMemberBelow_ChangeInfo.cs

@@ -1,5 +1,2 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
-public record class StructureMemberClipToMemberBelow_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class StructureMemberClipToMemberBelow_ChangeInfo(Guid GuidValue, bool ClipToMemberBelow) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberIsVisible_ChangeInfo.cs

@@ -1,6 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
 
-public record class StructureMemberIsVisible_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class StructureMemberIsVisible_ChangeInfo(Guid GuidValue, bool IsVisible) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberMaskIsVisible_ChangeInfo.cs

@@ -1,5 +1,2 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
-public record class StructureMemberMaskIsVisible_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class StructureMemberMaskIsVisible_ChangeInfo(Guid GuidValue, bool IsVisible) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberMask_ChangeInfo.cs

@@ -1,6 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
 
-public record class StructureMemberMask_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class StructureMemberMask_ChangeInfo(Guid GuidValue, bool HasMask) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberName_ChangeInfo.cs

@@ -1,6 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
 
-public record class StructureMemberName_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class StructureMemberName_ChangeInfo(Guid GuidValue, string Name) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Properties/StructureMemberOpacity_ChangeInfo.cs

@@ -1,6 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Properties;
 
-public record class StructureMemberOpacity_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+public record class StructureMemberOpacity_ChangeInfo(Guid GuidValue, float Opacity) : IChangeInfo;

+ 1 - 3
src/PixiEditor.ChangeableDocument/ChangeInfos/Root/Size_ChangeInfo.cs

@@ -1,5 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Root;
 
-public record class Size_ChangeInfo : IChangeInfo
-{
-}
+public record class Size_ChangeInfo(VecI Size, int VerticalSymmetryAxisX, int HorizontalSymmetryAxisY) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Root/SymmetryAxisPosition_ChangeInfo.cs

@@ -1,7 +1,4 @@
 using PixiEditor.ChangeableDocument.Enums;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Root;
-public record class SymmetryAxisPosition_ChangeInfo : IChangeInfo
-{
-    public SymmetryAxisDirection Direction { get; init; }
-}
+public record class SymmetryAxisPosition_ChangeInfo(SymmetryAxisDirection Direction, int NewPosition) : IChangeInfo;

+ 1 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Root/SymmetryAxisState_ChangeInfo.cs

@@ -1,7 +1,4 @@
 using PixiEditor.ChangeableDocument.Enums;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Root;
-public record class SymmetryAxisState_ChangeInfo : IChangeInfo
-{
-    public SymmetryAxisDirection Direction { get; init; }
-}
+public record class SymmetryAxisState_ChangeInfo(SymmetryAxisDirection Direction, bool State) : IChangeInfo;

+ 53 - 0
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateFolder_ChangeInfo.cs

@@ -0,0 +1,53 @@
+using System.Collections.Immutable;
+using PixiEditor.ChangeableDocument.Enums;
+
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
+public record class CreateFolder_ChangeInfo : CreateStructureMember_ChangeInfo
+{
+    public CreateFolder_ChangeInfo(
+        Guid parentGuid,
+        int index,
+        float opacity,
+        bool isVisible,
+        bool clipToMemberBelow,
+        string name,
+        BlendMode blendMode,
+        Guid guidValue,
+        bool hasMask,
+        bool maskIsVisible,
+        ImmutableList<CreateStructureMember_ChangeInfo> children) : base(parentGuid, index, opacity, isVisible, clipToMemberBelow, name, blendMode, guidValue, hasMask, maskIsVisible)
+    {
+        Children = children;
+    }
+
+    public ImmutableList<CreateStructureMember_ChangeInfo> Children { get; }
+
+    internal static CreateFolder_ChangeInfo FromFolder(Guid parentGuid, int index, Folder folder)
+    {
+        var builder = ImmutableList.CreateBuilder<CreateStructureMember_ChangeInfo>();
+        for (int i = 0; i < folder.Children.Count; i++)
+        {
+            var child = folder.Children[i];
+            CreateStructureMember_ChangeInfo info = child switch
+            {
+                Folder innerFolder => CreateFolder_ChangeInfo.FromFolder(folder.GuidValue, i, innerFolder),
+                Layer innerLayer => CreateLayer_ChangeInfo.FromLayer(folder.GuidValue, i, innerLayer),
+                _ => throw new NotSupportedException(),
+            };
+            builder.Add(info);
+        }
+        return new CreateFolder_ChangeInfo(
+            parentGuid,
+            index,
+            folder.Opacity,
+            folder.IsVisible,
+            folder.ClipToMemberBelow,
+            folder.Name,
+            folder.BlendMode,
+            folder.GuidValue,
+            folder.Mask is not null,
+            folder.MaskIsVisible,
+            builder.ToImmutable()
+            );
+    }
+}

+ 40 - 0
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateLayer_ChangeInfo.cs

@@ -0,0 +1,40 @@
+using PixiEditor.ChangeableDocument.Enums;
+
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
+public record class CreateLayer_ChangeInfo : CreateStructureMember_ChangeInfo
+{
+    public CreateLayer_ChangeInfo(
+        Guid parentGuid,
+        int index,
+        float opacity,
+        bool isVisible,
+        bool clipToMemberBelow,
+        string name,
+        BlendMode blendMode,
+        Guid guidValue,
+        bool hasMask,
+        bool maskIsVisible,
+        bool lockTransparency) : base(parentGuid, index, opacity, isVisible, clipToMemberBelow, name, blendMode, guidValue, hasMask, maskIsVisible)
+    {
+        LockTransparency = lockTransparency;
+    }
+
+    public bool LockTransparency { get; }
+
+    internal static CreateLayer_ChangeInfo FromLayer(Guid parentGuid, int index, Layer layer)
+    {
+        return new CreateLayer_ChangeInfo(
+            parentGuid,
+            index,
+            layer.Opacity,
+            layer.IsVisible,
+            layer.ClipToMemberBelow,
+            layer.Name,
+            layer.BlendMode,
+            layer.GuidValue,
+            layer.Mask is not null,
+            layer.MaskIsVisible,
+            layer.LockTransparency
+            );
+    }
+}

+ 15 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateStructureMember_ChangeInfo.cs

@@ -1,6 +1,16 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
+using PixiEditor.ChangeableDocument.Enums;
 
-public record class CreateStructureMember_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-}
+namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
+
+public abstract record class CreateStructureMember_ChangeInfo(
+    Guid ParentGuid,
+    int Index,
+    float Opacity,
+    bool IsVisible,
+    bool ClipToMemberBelow,
+    string Name,
+    BlendMode BlendMode,
+    Guid GuidValue,
+    bool HasMask,
+    bool MaskIsVisible
+) : IChangeInfo;

+ 1 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/DeleteStructureMember_ChangeInfo.cs

@@ -1,7 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
 
-public record class DeleteStructureMember_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-    public Guid ParentGuid { get; init; }
-}
+public record class DeleteStructureMember_ChangeInfo(Guid GuidValue, Guid ParentGuid) : IChangeInfo;

+ 1 - 6
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/MoveStructureMember_ChangeInfo.cs

@@ -1,8 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
 
-public record class MoveStructureMember_ChangeInfo : IChangeInfo
-{
-    public Guid GuidValue { get; init; }
-    public Guid ParentFromGuid { get; init; }
-    public Guid ParentToGuid { get; init; }
-}
+public record class MoveStructureMember_ChangeInfo(Guid GuidValue, Guid ParentFromGuid, Guid ParentToGuid, int NewIndex) : IChangeInfo;

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Folder.cs

@@ -5,6 +5,7 @@ namespace PixiEditor.ChangeableDocument.Changeables;
 
 internal class Folder : StructureMember, IReadOnlyFolder
 {
+    // Don't forget to update CreateFolder_ChangeInfo, DocumentUpdater.ProcessCreateStructureMember, and Folder.Clone when adding new properties
     public ImmutableList<StructureMember> Children { get; set; } = ImmutableList<StructureMember>.Empty;
     IReadOnlyList<IReadOnlyStructureMember> IReadOnlyFolder.Children => Children;
 

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Layer.cs

@@ -4,6 +4,7 @@ namespace PixiEditor.ChangeableDocument.Changeables;
 
 internal class Layer : StructureMember, IReadOnlyLayer
 {
+    // Don't forget to update CreateLayer_ChangeInfo, DocumentUpdater.ProcessCreateStructureMember and Layer.Clone when adding new properties
     public bool LockTransparency { get; set; } = false;
     public ChunkyImage LayerImage { get; set; }
     IReadOnlyChunkyImage IReadOnlyLayer.LayerImage => LayerImage;

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs

@@ -5,6 +5,8 @@ namespace PixiEditor.ChangeableDocument.Changeables;
 
 internal abstract class StructureMember : IChangeable, IReadOnlyStructureMember, IDisposable
 {
+    // Don't forget to update CreateStructureMember_ChangeInfo, CreateLayer_ChangeInfo, CreateFolder_ChangeInfo,
+    // DocumentUpdater.ProcessCreateStructureMember, Layer.Clone and Folder.Clone when adding new properties
     public float Opacity { get; set; } = 1f;
     public bool IsVisible { get; set; } = true;
     public bool ClipToMemberBelow { get; set; } = false;

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changes/Drawing/ApplyLayerMask_Change.cs

@@ -48,8 +48,8 @@ internal class ApplyLayerMask_Change : Change
         ignoreInUndo = false;
         return new List<IChangeInfo>
         {
-            new StructureMemberMask_ChangeInfo() { GuidValue = layerGuid },
-            new LayerImageChunks_ChangeInfo() { GuidValue = layerGuid, Chunks = affectedChunks }
+            new StructureMemberMask_ChangeInfo(layerGuid, false),
+            new LayerImageChunks_ChangeInfo(layerGuid, affectedChunks)
         };
     }
 
@@ -73,9 +73,9 @@ internal class ApplyLayerMask_Change : Change
 
         return new List<IChangeInfo>
         {
-            new StructureMemberMask_ChangeInfo() { GuidValue = layerGuid },
-            new LayerImageChunks_ChangeInfo() { GuidValue = layerGuid, Chunks = affectedChunksLayer },
-            new MaskChunks_ChangeInfo() { GuidValue = layerGuid, Chunks = affectedChunksMask }
+            new StructureMemberMask_ChangeInfo(layerGuid, true),
+            new LayerImageChunks_ChangeInfo(layerGuid, affectedChunksLayer),
+            new MaskChunks_ChangeInfo(layerGuid, affectedChunksMask)
         };
     }
 

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs

@@ -23,7 +23,7 @@ internal class ClearSelection_Change : Change
         target.Selection.SelectionPath = new SKPath();
 
         ignoreInUndo = false;
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath());
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -31,7 +31,7 @@ internal class ClearSelection_Change : Change
         target.Selection.SelectionPath.Dispose();
         target.Selection.SelectionPath = new SKPath(originalPath);
 
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath(originalPath));
     }
 
     public override void Dispose()

+ 3 - 12
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -1,5 +1,4 @@
-using PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
-using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 
@@ -73,11 +72,7 @@ internal class CombineStructureMembersOnto_Change : Change
         toDrawOn.LayerImage.CommitChanges();
 
         ignoreInUndo = false;
-        return new LayerImageChunks_ChangeInfo()
-        {
-            GuidValue = targetLayer,
-            Chunks = affectedChunks
-        };
+        return new LayerImageChunks_ChangeInfo(targetLayer, affectedChunks);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -91,11 +86,7 @@ internal class CombineStructureMembersOnto_Change : Change
         originalChunks.Dispose();
         originalChunks = null;
 
-        return new LayerImageChunks_ChangeInfo()
-        {
-            GuidValue = targetLayer,
-            Chunks = affectedChunks
-        };
+        return new LayerImageChunks_ChangeInfo(targetLayer, affectedChunks);
     }
 
     public override void Dispose()

+ 2 - 10
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawingChangeHelper.cs

@@ -48,16 +48,8 @@ internal static class DrawingChangeHelper
     {
         return drawOnMask switch
         {
-            false => new LayerImageChunks_ChangeInfo()
-            {
-                Chunks = affectedChunks,
-                GuidValue = memberGuid
-            },
-            true => new MaskChunks_ChangeInfo()
-            {
-                Chunks = affectedChunks,
-                GuidValue = memberGuid
-            },
+            false => new LayerImageChunks_ChangeInfo(memberGuid, affectedChunks),
+            true => new MaskChunks_ChangeInfo(memberGuid, affectedChunks),
         };
     }
 }

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Drawing/ShiftLayer_UpdateableChange.cs

@@ -49,13 +49,13 @@ internal class ShiftLayer_UpdateableChange : UpdateableChange
         image.CommitChanges();
 
         ignoreInUndo = delta.TaxicabLength == 0;
-        return new LayerImageChunks_ChangeInfo() { GuidValue = layerGuid, Chunks = chunks };
+        return new LayerImageChunks_ChangeInfo(layerGuid, chunks);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
     {
         var chunks = DrawShiftedLayer(target);
-        return new LayerImageChunks_ChangeInfo() { GuidValue = layerGuid, Chunks = chunks };
+        return new LayerImageChunks_ChangeInfo(layerGuid, chunks);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -68,7 +68,7 @@ internal class ShiftLayer_UpdateableChange : UpdateableChange
         image.CommitChanges();
         originalLayerChunks.Dispose();
         originalLayerChunks = null;
-        return new LayerImageChunks_ChangeInfo() { GuidValue = layerGuid, Chunks = affected };
+        return new LayerImageChunks_ChangeInfo(layerGuid, affected);
     }
 
     public override void Dispose()

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Properties/CreateStructureMemberMask_Change.cs

@@ -28,7 +28,7 @@ internal class CreateStructureMemberMask_Change : Change
         member.Mask = new ChunkyImage(target.Size);
 
         ignoreInUndo = false;
-        return new StructureMemberMask_ChangeInfo() { GuidValue = targetMember };
+        return new StructureMemberMask_ChangeInfo(targetMember, true);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -38,6 +38,6 @@ internal class CreateStructureMemberMask_Change : Change
             throw new InvalidOperationException("Cannot delete the mask; the target member has no mask");
         member.Mask.Dispose();
         member.Mask = null;
-        return new StructureMemberMask_ChangeInfo() { GuidValue = targetMember };
+        return new StructureMemberMask_ChangeInfo(targetMember, false);
     }
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Properties/DeleteStructureMemberMask_Change.cs

@@ -31,7 +31,7 @@ internal class DeleteStructureMemberMask_Change : Change
         member.Mask = null;
 
         ignoreInUndo = false;
-        return new StructureMemberMask_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberMask_ChangeInfo(memberGuid, false);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -41,7 +41,7 @@ internal class DeleteStructureMemberMask_Change : Change
             throw new InvalidOperationException("Cannot revert mask deletion; The target member already has a mask");
         member.Mask = storedMask!.CloneFromCommitted();
 
-        return new StructureMemberMask_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberMask_ChangeInfo(memberGuid, true);
     }
 
     public override void Dispose()

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Properties/LayerLockTransparency_Change.cs

@@ -29,17 +29,17 @@ internal class LayerLockTransparency_Change : Change
     {
         ((Layer)target.FindMemberOrThrow(layerGuid)).LockTransparency = newValue;
         ignoreInUndo = false;
-        return new LayerLockTransparency_ChangeInfo() { GuidValue = layerGuid };
+        return new LayerLockTransparency_ChangeInfo(layerGuid, newValue);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         ((Layer)target.FindMemberOrThrow(layerGuid)).LockTransparency = originalValue;
-        return new LayerLockTransparency_ChangeInfo() { GuidValue = layerGuid };
+        return new LayerLockTransparency_ChangeInfo(layerGuid, originalValue);
     }
 
     public override bool IsMergeableWith(Change other)
     {
-        return other is LayerLockTransparency_Change;
+        return other is LayerLockTransparency_Change change && change.layerGuid == layerGuid;
     }
 }

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberBlendMode_Change.cs

@@ -29,18 +29,18 @@ internal class StructureMemberBlendMode_Change : Change
         var member = target.FindMemberOrThrow(targetGuid);
         member.BlendMode = newBlendMode;
         ignoreInUndo = false;
-        return new StructureMemberBlendMode_ChangeInfo() { GuidValue = targetGuid };
+        return new StructureMemberBlendMode_ChangeInfo(targetGuid, newBlendMode);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         var member = target.FindMemberOrThrow(targetGuid);
         member.BlendMode = originalBlendMode;
-        return new StructureMemberBlendMode_ChangeInfo() { GuidValue = targetGuid };
+        return new StructureMemberBlendMode_ChangeInfo(targetGuid, originalBlendMode);
     }
 
     public override bool IsMergeableWith(Change other)
     {
-        return other is StructureMemberBlendMode_Change;
+        return other is StructureMemberBlendMode_Change change && change.targetGuid == targetGuid;
     }
 }

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberClipToMemberBelow_Change.cs

@@ -28,18 +28,18 @@ internal class StructureMemberClipToMemberBelow_Change : Change
         var member = target.FindMemberOrThrow(memberGuid);
         member.ClipToMemberBelow = newValue;
         ignoreInUndo = false;
-        return new StructureMemberClipToMemberBelow_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberClipToMemberBelow_ChangeInfo(memberGuid, newValue);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         var member = target.FindMemberOrThrow(memberGuid);
         member.ClipToMemberBelow = originalValue;
-        return new StructureMemberClipToMemberBelow_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberClipToMemberBelow_ChangeInfo(memberGuid, originalValue);
     }
 
     public override bool IsMergeableWith(Change other)
     {
-        return other is StructureMemberClipToMemberBelow_Change;
+        return other is StructureMemberClipToMemberBelow_Change change && change.memberGuid == memberGuid;
     }
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberIsVisible_Change.cs

@@ -28,12 +28,12 @@ internal class StructureMemberIsVisible_Change : Change
         // don't record layer/folder visibility changes - it's just more convenient this way
         ignoreInUndo = true;
         target.FindMemberOrThrow(targetMember).IsVisible = newIsVisible;
-        return new StructureMemberIsVisible_ChangeInfo() { GuidValue = targetMember };
+        return new StructureMemberIsVisible_ChangeInfo(targetMember, newIsVisible);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         target.FindMemberOrThrow(targetMember).IsVisible = originalIsVisible!.Value;
-        return new StructureMemberIsVisible_ChangeInfo() { GuidValue = targetMember };
+        return new StructureMemberIsVisible_ChangeInfo(targetMember, (bool)originalIsVisible);
     }
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberMaskIsVisible_Change.cs

@@ -30,14 +30,14 @@ internal class StructureMemberMaskIsVisible_Change : Change
         var member = target.FindMemberOrThrow(memberGuid);
         member.MaskIsVisible = newMaskIsVisible;
         ignoreInUndo = false;
-        return new StructureMemberMaskIsVisible_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberMaskIsVisible_ChangeInfo(memberGuid, newMaskIsVisible);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         var member = target.FindMemberOrThrow(memberGuid);
         member.MaskIsVisible = originalMaskIsVisible;
-        return new StructureMemberMaskIsVisible_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberMaskIsVisible_ChangeInfo(memberGuid, originalMaskIsVisible);
     }
 
     public override bool IsMergeableWith(Change other)

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberName_Change.cs

@@ -29,7 +29,7 @@ internal class StructureMemberName_Change : Change
         target.FindMemberOrThrow(targetMember).Name = newName;
 
         ignoreInUndo = false;
-        return new StructureMemberName_ChangeInfo() { GuidValue = targetMember };
+        return new StructureMemberName_ChangeInfo(targetMember, newName);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -37,7 +37,7 @@ internal class StructureMemberName_Change : Change
         if (originalName is null)
             throw new InvalidOperationException("No name to revert to");
         target.FindMemberOrThrow(targetMember).Name = originalName;
-        return new StructureMemberName_ChangeInfo() { GuidValue = targetMember };
+        return new StructureMemberName_ChangeInfo(targetMember, originalName);
     }
 
     public override bool IsMergeableWith(Change other)

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberOpacity_UpdateableChange.cs

@@ -45,7 +45,7 @@ internal class StructureMemberOpacity_UpdateableChange : UpdateableChange
         member.Opacity = newOpacity;
 
         ignoreInUndo = false;
-        return new StructureMemberOpacity_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberOpacity_ChangeInfo(memberGuid, newOpacity);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document document)
@@ -56,7 +56,7 @@ internal class StructureMemberOpacity_UpdateableChange : UpdateableChange
         var member = document.FindMemberOrThrow(memberGuid);
         member.Opacity = originalOpacity;
 
-        return new StructureMemberOpacity_ChangeInfo() { GuidValue = memberGuid };
+        return new StructureMemberOpacity_ChangeInfo(memberGuid, originalOpacity);
     }
 
     public override bool IsMergeableWith(Change other)

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Root/ResizeCanvas_Change.cs

@@ -59,7 +59,7 @@ internal class ResizeCanvas_Change : Change
             layer.Mask.CommitChanges();
         });
         ignoreInUndo = false;
-        return new Size_ChangeInfo();
+        return new Size_ChangeInfo(newSize, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -86,7 +86,7 @@ internal class ResizeCanvas_Change : Change
             stored.Value.Dispose();
         deletedChunks = new();
 
-        return new Size_ChangeInfo();
+        return new Size_ChangeInfo(originalSize, originalVerAxisX, originalHorAxisY);
     }
 
     public override void Dispose()

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Root/SymmetryAxisPosition_UpdateableChange.cs

@@ -46,13 +46,13 @@ internal class SymmetryAxisPosition_UpdateableChange : UpdateableChange
     {
         ignoreInUndo = originalPos == newPos;
         SetPosition(target, newPos);
-        return new SymmetryAxisPosition_ChangeInfo() { Direction = direction };
+        return new SymmetryAxisPosition_ChangeInfo(direction, newPos);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
     {
         SetPosition(target, newPos);
-        return new SymmetryAxisPosition_ChangeInfo() { Direction = direction };
+        return new SymmetryAxisPosition_ChangeInfo(direction, newPos);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
@@ -60,7 +60,7 @@ internal class SymmetryAxisPosition_UpdateableChange : UpdateableChange
         if (originalPos == newPos)
             return new None();
         SetPosition(target, originalPos);
-        return new SymmetryAxisPosition_ChangeInfo() { Direction = direction };
+        return new SymmetryAxisPosition_ChangeInfo(direction, originalPos);
     }
 
     public override bool IsMergeableWith(Change other)

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Root/SymmetryAxisState_Change.cs

@@ -42,13 +42,13 @@ internal class SymmetryAxisState_Change : Change
     {
         SetState(target, newEnabled);
         ignoreInUndo = false;
-        return new SymmetryAxisState_ChangeInfo() { Direction = direction };
+        return new SymmetryAxisState_ChangeInfo(direction, newEnabled);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         SetState(target, originalEnabled);
-        return new SymmetryAxisState_ChangeInfo() { Direction = direction };
+        return new SymmetryAxisState_ChangeInfo(direction, originalEnabled);
     }
 
     public override bool IsMergeableWith(Change other)

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Selection/SelectLasso_UpdateableChange.cs

@@ -42,7 +42,7 @@ internal class SelectLasso_UpdateableChange : UpdateableChange
         }
         toDispose.Dispose();
 
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath(target.Selection.SelectionPath));
     }
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, out bool ignoreInUndo)
     {
@@ -60,7 +60,7 @@ internal class SelectLasso_UpdateableChange : UpdateableChange
         var toDispose = target.Selection.SelectionPath;
         target.Selection.SelectionPath = new(originalPath);
         toDispose.Dispose();
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath(target.Selection.SelectionPath));
     }
 
     public override void Dispose()

+ 2 - 3
src/PixiEditor.ChangeableDocument/Changes/Selection/SelectRectangle_UpdateableChange.cs

@@ -43,7 +43,7 @@ internal class SelectRectangle_UpdateableChange : UpdateableChange
             target.Selection.SelectionPath = originalPath!.Op(rectPath, mode.ToSKPathOp());
         toDispose.Dispose();
 
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath(target.Selection.SelectionPath));
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
@@ -60,10 +60,9 @@ internal class SelectRectangle_UpdateableChange : UpdateableChange
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
-        var changes = new Selection_ChangeInfo();
         target.Selection.SelectionPath.Dispose();
         target.Selection.SelectionPath = new SKPath(originalPath);
-        return changes;
+        return new Selection_ChangeInfo(new SKPath(target.Selection.SelectionPath));
     }
 
     public override void Dispose()

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Selection/TransformSelectionPath_UpdateableChange.cs

@@ -42,7 +42,7 @@ internal class TransformSelectionPath_UpdateableChange : UpdateableChange
         target.Selection.SelectionPath = newPath;
         toDispose.Dispose();
 
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath(target.Selection.SelectionPath));
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, out bool ignoreInUndo)
@@ -61,7 +61,7 @@ internal class TransformSelectionPath_UpdateableChange : UpdateableChange
         var toDispose = target.Selection.SelectionPath;
         target.Selection.SelectionPath = new SKPath(originalPath);
         toDispose.Dispose();
-        return new Selection_ChangeInfo();
+        return new Selection_ChangeInfo(new SKPath(target.Selection.SelectionPath));
     }
 
     public override void Dispose()

+ 8 - 3
src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

@@ -35,13 +35,18 @@ internal class CreateStructureMember_Change : Change
         {
             StructureMemberType.Layer => new Layer(document.Size) { GuidValue = newMemberGuid },
             StructureMemberType.Folder => new Folder() { GuidValue = newMemberGuid },
-            _ => throw new InvalidOperationException("Cannon create member of type " + type.ToString())
+            _ => throw new NotSupportedException(),
         };
 
         folder.Children = folder.Children.Insert(parentFolderIndex, member);
 
         ignoreInUndo = false;
-        return new CreateStructureMember_ChangeInfo() { GuidValue = newMemberGuid };
+        return type switch
+        {
+            StructureMemberType.Layer => CreateLayer_ChangeInfo.FromLayer(parentFolderGuid, parentFolderIndex, (Layer)member),
+            StructureMemberType.Folder => CreateFolder_ChangeInfo.FromFolder(parentFolderGuid, parentFolderIndex, (Folder)member),
+            _ => throw new NotSupportedException(),
+        };
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document document)
@@ -51,6 +56,6 @@ internal class CreateStructureMember_Change : Change
         child.Dispose();
         folder.Children = folder.Children.RemoveAt(folder.Children.FindIndex(child => child.GuidValue == newMemberGuid));
 
-        return new DeleteStructureMember_ChangeInfo() { GuidValue = newMemberGuid, ParentGuid = parentFolderGuid };
+        return new DeleteStructureMember_ChangeInfo(newMemberGuid, parentFolderGuid);
     }
 }

+ 9 - 3
src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs

@@ -33,15 +33,21 @@ internal class DeleteStructureMember_Change : Change
         parent.Children = parent.Children.Remove(member);
         member.Dispose();
         ignoreInUndo = false;
-        return new DeleteStructureMember_ChangeInfo() { GuidValue = memberGuid, ParentGuid = parentGuid };
+        return new DeleteStructureMember_ChangeInfo(memberGuid, parentGuid);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document doc)
     {
         var parent = (Folder)doc.FindMemberOrThrow(parentGuid);
 
-        parent.Children = parent.Children.Insert(originalIndex, savedCopy!.Clone());
-        return new CreateStructureMember_ChangeInfo() { GuidValue = memberGuid };
+        var copy = savedCopy!.Clone();
+        parent.Children = parent.Children.Insert(originalIndex, copy);
+        return copy switch
+        {
+            Layer => CreateLayer_ChangeInfo.FromLayer(parentGuid, originalIndex, (Layer)copy),
+            Folder => CreateFolder_ChangeInfo.FromFolder(parentGuid, originalIndex, (Folder)copy),
+            _ => throw new NotSupportedException(),
+        }; ;
     }
 
     public override void Dispose()

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs

@@ -44,12 +44,12 @@ internal class MoveStructureMember_Change : Change
     {
         Move(target, memberGuid, targetFolderGuid, targetFolderIndex);
         ignoreInUndo = false;
-        return new MoveStructureMember_ChangeInfo() { GuidValue = memberGuid, ParentFromGuid = originalFolderGuid, ParentToGuid = targetFolderGuid };
+        return new MoveStructureMember_ChangeInfo(memberGuid, originalFolderGuid, targetFolderGuid, targetFolderIndex);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         Move(target, memberGuid, originalFolderGuid, originalFolderIndex);
-        return new MoveStructureMember_ChangeInfo() { GuidValue = memberGuid, ParentFromGuid = targetFolderGuid, ParentToGuid = originalFolderGuid };
+        return new MoveStructureMember_ChangeInfo(memberGuid, targetFolderGuid, originalFolderGuid, originalFolderIndex);
     }
 }

+ 9 - 3
src/PixiEditorPrototype/Models/DocumentStructureHelper.cs

@@ -19,24 +19,30 @@ internal class DocumentStructureHelper
     {
         if (doc.SelectedStructureMember is null)
         {
+            var guid = Guid.NewGuid();
             //put member on top
-            helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, Guid.NewGuid(), doc.StructureRoot.Children.Count, type));
+            helpers.ActionAccumulator.AddActions(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, guid, doc.StructureRoot.Children.Count, type));
+            helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(guid, type == StructureMemberType.Layer ? "New Layer" : "New Folder"));
             return;
         }
         if (doc.SelectedStructureMember is FolderViewModel folder)
         {
+            var guid = Guid.NewGuid();
             //put member inside folder on top
-            helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(folder.GuidValue, Guid.NewGuid(), folder.Children.Count, type));
+            helpers.ActionAccumulator.AddActions(new CreateStructureMember_Action(folder.GuidValue, guid, folder.Children.Count, type));
+            helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(guid, type == StructureMemberType.Layer ? "New Layer" : "New Folder"));
             return;
         }
         if (doc.SelectedStructureMember is LayerViewModel layer)
         {
+            var guid = Guid.NewGuid();
             //put member above the layer
             var path = FindPath(layer.GuidValue);
             if (path.Count < 2)
                 throw new InvalidOperationException("Couldn't find a path to the selected member");
             var parent = (FolderViewModel)path[1];
-            helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(parent.GuidValue, Guid.NewGuid(), parent.Children.IndexOf(layer) + 1, type));
+            helpers.ActionAccumulator.AddActions(new CreateStructureMember_Action(parent.GuidValue, guid, parent.Children.IndexOf(layer) + 1, type));
+            helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(guid, type == StructureMemberType.Layer ? "New Layer" : "New Folder"));
             return;
         }
         throw new ArgumentException("Unknown member type: " + type.ToString());

+ 58 - 41
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
 using PixiEditor.ChangeableDocument.ChangeInfos.Properties;
@@ -89,46 +88,46 @@ internal class DocumentUpdater
     private void ProcessMaskIsVisible(StructureMemberMaskIsVisible_ChangeInfo info)
     {
         var member = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        member.RaisePropertyChanged(nameof(member.MaskIsVisible));
+        member.SetMaskIsVisible(info.IsVisible);
     }
 
     private void ProcessClipToMemberBelow(StructureMemberClipToMemberBelow_ChangeInfo info)
     {
         var member = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        member.RaisePropertyChanged(nameof(member.ClipToMemberBelowEnabled));
+        member.SetClipToMemberBelowEnabled(info.ClipToMemberBelow);
     }
 
     private void ProcessSymmetryPosition(SymmetryAxisPosition_ChangeInfo info)
     {
         if (info.Direction == SymmetryAxisDirection.Horizontal)
-            doc.RaisePropertyChanged(nameof(doc.HorizontalSymmetryAxisY));
+            doc.SetHorizontalSymmetryAxisY(info.NewPosition);
         else if (info.Direction == SymmetryAxisDirection.Vertical)
-            doc.RaisePropertyChanged(nameof(doc.VerticalSymmetryAxisX));
+            doc.SetVerticalSymmetryAxisX(info.NewPosition);
     }
 
     private void ProcessSymmetryState(SymmetryAxisState_ChangeInfo info)
     {
         if (info.Direction == SymmetryAxisDirection.Horizontal)
-            doc.RaisePropertyChanged(nameof(doc.HorizontalSymmetryAxisEnabled));
+            doc.SetHorizontalSymmetryAxisEnabled(info.State);
         else if (info.Direction == SymmetryAxisDirection.Vertical)
-            doc.RaisePropertyChanged(nameof(doc.VerticalSymmetryAxisEnabled));
+            doc.SetVerticalSymmetryAxisEnabled(info.State);
     }
 
     private void ProcessSelection(Selection_ChangeInfo info)
     {
-        doc.RaisePropertyChanged(nameof(doc.SelectionPath));
+        doc.SetSelectionPath(info.NewPath);
     }
 
     private void ProcessLayerLockTransparency(LayerLockTransparency_ChangeInfo info)
     {
         var layer = (LayerViewModel)helper.StructureHelper.FindOrThrow(info.GuidValue);
-        layer.RaisePropertyChanged(nameof(layer.LockTransparency));
+        layer.SetLockTransparency(info.LockTransparency);
     }
 
     private void ProcessStructureMemberBlendMode(StructureMemberBlendMode_ChangeInfo info)
     {
         var memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVm.RaisePropertyChanged(nameof(memberVm.BlendMode));
+        memberVm.SetBlendMode(info.BlendMode);
     }
 
     private void ProcessStructureMemberMask(StructureMemberMask_ChangeInfo info)
@@ -137,13 +136,14 @@ internal class DocumentUpdater
         memberVm.MaskPreviewSurface?.Dispose();
         memberVm.MaskPreviewSurface = null;
         memberVm.MaskPreviewBitmap = null;
-        var size = StructureMemberViewModel.CalculatePreviewSize(new(doc.Width, doc.Height));
-        if (memberVm.HasMask)
+
+        if (info.HasMask)
         {
+            var size = StructureMemberViewModel.CalculatePreviewSize(doc.SizeBindable);
             memberVm.MaskPreviewBitmap = CreateBitmap(size);
             memberVm.MaskPreviewSurface = CreateSKSurface(memberVm.MaskPreviewBitmap);
         }
-        memberVm.RaisePropertyChanged(nameof(memberVm.HasMask));
+        memberVm.SetHasMask(info.HasMask);
         memberVm.RaisePropertyChanged(nameof(memberVm.MaskPreviewBitmap));
     }
 
@@ -169,7 +169,7 @@ internal class DocumentUpdater
             member.MaskPreviewSurface?.Dispose();
             member.MaskPreviewSurface = null;
             member.MaskPreviewBitmap = null;
-            if (member.HasMask)
+            if (member.HasMaskBindable)
             {
                 member.MaskPreviewBitmap = CreateBitmap(newSize);
                 member.MaskPreviewSurface = CreateSKSurface(member.MaskPreviewBitmap);
@@ -185,23 +185,23 @@ internal class DocumentUpdater
 
     private void ProcessSize(Size_ChangeInfo info)
     {
-        var size = helper.Tracker.Document.Size;
         Dictionary<ChunkResolution, WriteableBitmap> newBitmaps = new();
         foreach (var (res, surf) in doc.Surfaces)
         {
             surf.Dispose();
-            newBitmaps[res] = CreateBitmap((VecI)(size * res.Multiplier()));
+            newBitmaps[res] = CreateBitmap((VecI)(info.Size * res.Multiplier()));
             doc.Surfaces[res] = CreateSKSurface(newBitmaps[res]);
         }
 
         doc.Bitmaps = newBitmaps;
+
+        doc.SetSize(info.Size);
+        doc.SetVerticalSymmetryAxisX(info.VerticalSymmetryAxisX);
+        doc.SetHorizontalSymmetryAxisY(info.HorizontalSymmetryAxisY);
+
         doc.RaisePropertyChanged(nameof(doc.Bitmaps));
-        doc.RaisePropertyChanged(nameof(doc.Width));
-        doc.RaisePropertyChanged(nameof(doc.Height));
-        doc.RaisePropertyChanged(nameof(doc.HorizontalSymmetryAxisY));
-        doc.RaisePropertyChanged(nameof(doc.VerticalSymmetryAxisX));
 
-        var previewSize = StructureMemberViewModel.CalculatePreviewSize(size);
+        var previewSize = StructureMemberViewModel.CalculatePreviewSize(info.Size);
         UpdateMemberBitmapsRecursively(doc.StructureRoot, previewSize);
     }
 
@@ -220,25 +220,44 @@ internal class DocumentUpdater
 
     private void ProcessCreateStructureMember(CreateStructureMember_ChangeInfo info)
     {
-        var (member, parentFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
-        var parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(parentFolder.GuidValue);
+        var parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(info.ParentGuid);
 
-        int index = parentFolder.Children.IndexOf(member);
-
-        StructureMemberViewModel memberVM = member switch
+        StructureMemberViewModel memberVM;
+        if (info is CreateLayer_ChangeInfo layerInfo)
+        {
+            memberVM = new LayerViewModel(doc, helper, info.GuidValue);
+            ((LayerViewModel)memberVM).SetLockTransparency(layerInfo.LockTransparency);
+        }
+        else if (info is CreateFolder_ChangeInfo)
         {
-            IReadOnlyLayer layer => new LayerViewModel(doc, helper, layer),
-            IReadOnlyFolder folder => new FolderViewModel(doc, helper, folder),
-            _ => throw new InvalidOperationException("Unsupposed member type")
-        };
+            memberVM = new FolderViewModel(doc, helper, info.GuidValue);
+        }
+        else
+        {
+            throw new NotSupportedException();
+        }
+        memberVM.SetOpacity(info.Opacity);
+        memberVM.SetIsVisible(info.IsVisible);
+        memberVM.SetClipToMemberBelowEnabled(info.ClipToMemberBelow);
+        memberVM.SetName(info.Name);
+        memberVM.SetHasMask(info.HasMask);
+        memberVM.SetMaskIsVisible(info.MaskIsVisible);
+        memberVM.SetBlendMode(info.BlendMode);
+
+        if (info.HasMask)
+        {
+            var size = StructureMemberViewModel.CalculatePreviewSize(doc.SizeBindable);
+            memberVM.MaskPreviewBitmap = CreateBitmap(size);
+            memberVM.MaskPreviewSurface = CreateSKSurface(memberVM.MaskPreviewBitmap);
+        }
 
-        parentFolderVM.Children.Insert(index, memberVM);
+        parentFolderVM.Children.Insert(info.Index, memberVM);
 
-        if (member is IReadOnlyFolder folder2)
+        if (info is CreateFolder_ChangeInfo folderInfo)
         {
-            foreach (IReadOnlyStructureMember child in folder2.Children)
+            foreach (CreateStructureMember_ChangeInfo childInfo in folderInfo.Children)
             {
-                ProcessCreateStructureMember(new CreateStructureMember_ChangeInfo() { GuidValue = child.GuidValue });
+                ProcessCreateStructureMember(childInfo);
             }
         }
     }
@@ -252,30 +271,28 @@ internal class DocumentUpdater
     private void ProcessUpdateStructureMemberIsVisible(StructureMemberIsVisible_ChangeInfo info)
     {
         var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVM.RaisePropertyChanged(nameof(memberVM.IsVisible));
+        memberVM.SetIsVisible(info.IsVisible);
     }
 
     private void ProcessUpdateStructureMemberName(StructureMemberName_ChangeInfo info)
     {
         var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVM.RaisePropertyChanged(nameof(memberVM.Name));
+        memberVM.SetName(info.Name);
     }
 
     private void ProcessUpdateStructureMemberOpacity(StructureMemberOpacity_ChangeInfo info)
     {
         var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-        memberVM.RaisePropertyChanged(nameof(memberVM.Opacity));
+        memberVM.SetOpacity(info.Opacity);
     }
 
     private void ProcessMoveStructureMember(MoveStructureMember_ChangeInfo info)
     {
         var (memberVM, curFolderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
-        var (member, targetFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
 
-        int index = targetFolder.Children.IndexOf(member);
-        var targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(targetFolder.GuidValue);
+        var targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(info.ParentToGuid);
 
         curFolderVM.Children.Remove(memberVM);
-        targetFolderVM.Children.Insert(index, memberVM);
+        targetFolderVM.Children.Insert(info.NewIndex, memberVM);
     }
 }

+ 2 - 2
src/PixiEditorPrototype/Models/Rendering/WriteableBitmapUpdater.cs

@@ -111,7 +111,7 @@ internal class WriteableBitmapUpdater
 
         var (imagePreviewChunksToRerender, maskPreviewChunksToRerender) = FindPreviewChunksToRerender(chunkGatherer, !updatePreviews);
         var previewSize = StructureMemberViewModel.CalculatePreviewSize(helpers.Tracker.Document.Size);
-        float scaling = (float)previewSize.X / doc.Width;
+        float scaling = (float)previewSize.X / doc.SizeBindable.X;
         UpdateImagePreviews(imagePreviewChunksToRerender, scaling, infos);
         UpdateMaskPreviews(maskPreviewChunksToRerender, scaling, infos);
 
@@ -172,7 +172,7 @@ internal class WriteableBitmapUpdater
         foreach (var (guid, chunks) in maskPreviewChunks)
         {
             var memberVM = helpers.StructureHelper.Find(guid);
-            if (memberVM is null || !memberVM.HasMask)
+            if (memberVM is null || !memberVM.HasMaskBindable)
                 continue;
 
             var member = helpers.Tracker.Document.FindMemberOrThrow(guid);

+ 5 - 5
src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml

@@ -50,14 +50,14 @@
                     </Image>
                     <sym:SymmetryOverlay 
                         ZoomboxScale="{Binding Zoombox.Scale}"
-                        HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabled}"
-                        VerticalAxisVisible="{Binding Document.VerticalSymmetryAxisEnabled}"
-                        HorizontalAxisY="{Binding Document.HorizontalSymmetryAxisY, Mode=OneWay}"
-                        VerticalAxisX="{Binding Document.VerticalSymmetryAxisX, Mode=OneWay}"
+                        HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabledBindable}"
+                        VerticalAxisVisible="{Binding Document.VerticalSymmetryAxisEnabledBindable}"
+                        HorizontalAxisY="{Binding Document.HorizontalSymmetryAxisYBindable, Mode=OneWay}"
+                        VerticalAxisX="{Binding Document.VerticalSymmetryAxisXBindable, Mode=OneWay}"
                         DragCommand="{Binding Document.DragSymmetryCommand}"
                         DragEndCommand="{Binding Document.EndDragSymmetryCommand}"
                         />
-                    <cust:SelectionOverlay Path="{Binding Document.SelectionPath}" ZoomboxScale="{Binding Zoombox.Scale}"/>
+                    <cust:SelectionOverlay Path="{Binding Document.SelectionPathBindable}" ZoomboxScale="{Binding Zoombox.Scale}"/>
                     <to:TransformOverlay 
                         HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                         Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={StaticResource BoolToVisibilityConverter}}"

+ 119 - 72
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -19,36 +19,8 @@ namespace PixiEditorPrototype.ViewModels;
 
 internal class DocumentViewModel : INotifyPropertyChanged
 {
-    private StructureMemberViewModel? selectedStructureMember;
-    public StructureMemberViewModel? SelectedStructureMember
-    {
-        get => selectedStructureMember;
-        private set
-        {
-            selectedStructureMember = value;
-            PropertyChanged?.Invoke(this, new(nameof(SelectedStructureMember)));
-        }
-    }
-
-    public Dictionary<ChunkResolution, WriteableBitmap> Bitmaps { get; set; } = new()
-    {
-        [ChunkResolution.Full] = new WriteableBitmap(64, 64, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Half] = new WriteableBitmap(32, 32, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Quarter] = new WriteableBitmap(16, 16, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Eighth] = new WriteableBitmap(8, 8, 96, 96, PixelFormats.Pbgra32, null),
-    };
-
-    public Dictionary<ChunkResolution, SKSurface> Surfaces { get; set; } = new();
-
     public event PropertyChangedEventHandler? PropertyChanged;
 
-    public void RaisePropertyChanged(string name)
-    {
-        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
-    }
-
-    public FolderViewModel StructureRoot { get; }
-    public DocumentTransformViewModel TransformViewModel { get; }
     public RelayCommand? UndoCommand { get; }
     public RelayCommand? RedoCommand { get; }
     public RelayCommand? ClearSelectionCommand { get; }
@@ -71,30 +43,118 @@ internal class DocumentViewModel : INotifyPropertyChanged
     public RelayCommand? TransformSelectionPathCommand { get; }
     public RelayCommand? TransformSelectedAreaCommand { get; }
 
-    public int Width => Helpers.Tracker.Document.Size.X;
-    public int Height => Helpers.Tracker.Document.Size.Y;
-    public SKPath SelectionPath => Helpers.Tracker.Document.Selection.SelectionPath;
-    public Guid GuidValue { get; } = Guid.NewGuid();
-    public int HorizontalSymmetryAxisY => Helpers.Tracker.Document.HorizontalSymmetryAxisY;
-    public int VerticalSymmetryAxisX => Helpers.Tracker.Document.VerticalSymmetryAxisX;
-    public bool HorizontalSymmetryAxisEnabled
+    private VecI size = new VecI(64, 64);
+    public void SetSize(VecI size)
     {
-        get => Helpers.Tracker.Document.HorizontalSymmetryAxisEnabled;
+        this.size = size;
+        RaisePropertyChanged(nameof(SizeBindable));
+        RaisePropertyChanged(nameof(Width));
+        RaisePropertyChanged(nameof(Height));
+    }
+    public VecI SizeBindable => size;
+
+    private SKPath selectionPath = new SKPath();
+    public void SetSelectionPath(SKPath selectionPath)
+    {
+        (var toDispose, this.selectionPath) = (this.selectionPath, selectionPath);
+        toDispose.Dispose();
+        RaisePropertyChanged(nameof(SelectionPathBindable));
+    }
+    public SKPath SelectionPathBindable => selectionPath;
+
+    private int horizontalSymmetryAxisY;
+    public void SetHorizontalSymmetryAxisY(int horizontalSymmetryAxisY)
+    {
+        this.horizontalSymmetryAxisY = horizontalSymmetryAxisY;
+        RaisePropertyChanged(nameof(HorizontalSymmetryAxisYBindable));
+    }
+    public int HorizontalSymmetryAxisYBindable => horizontalSymmetryAxisY;
+
+    private int verticalSymmetryAxisX;
+    public void SetVerticalSymmetryAxisX(int verticalSymmetryAxisX)
+    {
+        this.verticalSymmetryAxisX = verticalSymmetryAxisX;
+        RaisePropertyChanged(nameof(VerticalSymmetryAxisXBindable));
+    }
+    public int VerticalSymmetryAxisXBindable => verticalSymmetryAxisX;
+
+    private bool horizontalSymmetryAxisEnabled;
+    public void SetHorizontalSymmetryAxisEnabled(bool horizontalSymmetryAxisEnabled)
+    {
+        this.horizontalSymmetryAxisEnabled = horizontalSymmetryAxisEnabled;
+        RaisePropertyChanged(nameof(HorizontalSymmetryAxisEnabledBindable));
+    }
+    public bool HorizontalSymmetryAxisEnabledBindable
+    {
+        get => horizontalSymmetryAxisEnabled;
         set => Helpers.ActionAccumulator.AddFinishedActions(new SymmetryAxisState_Action(SymmetryAxisDirection.Horizontal, value));
     }
-    public bool VerticalSymmetryAxisEnabled
+
+    private bool verticalSymmetryAxisEnabled;
+    public void SetVerticalSymmetryAxisEnabled(bool verticalSymmetryAxisEnabled)
     {
-        get => Helpers.Tracker.Document.VerticalSymmetryAxisEnabled;
+        this.verticalSymmetryAxisEnabled = verticalSymmetryAxisEnabled;
+        RaisePropertyChanged(nameof(VerticalSymmetryAxisEnabledBindable));
+    }
+    public bool VerticalSymmetryAxisEnabledBindable
+    {
+        get => verticalSymmetryAxisEnabled;
         set => Helpers.ActionAccumulator.AddFinishedActions(new SymmetryAxisState_Action(SymmetryAxisDirection.Vertical, value));
     }
 
+    public Guid GuidValue { get; } = Guid.NewGuid();
+    public int Width => size.X;
+    public int Height => size.Y;
+
+    private StructureMemberViewModel? selectedStructureMember;
+    public StructureMemberViewModel? SelectedStructureMember
+    {
+        get => selectedStructureMember;
+        private set
+        {
+            selectedStructureMember = value;
+            PropertyChanged?.Invoke(this, new(nameof(SelectedStructureMember)));
+        }
+    }
+
+    public Dictionary<ChunkResolution, WriteableBitmap> Bitmaps { get; set; } = new()
+    {
+        [ChunkResolution.Full] = new WriteableBitmap(64, 64, 96, 96, PixelFormats.Pbgra32, null),
+        [ChunkResolution.Half] = new WriteableBitmap(32, 32, 96, 96, PixelFormats.Pbgra32, null),
+        [ChunkResolution.Quarter] = new WriteableBitmap(16, 16, 96, 96, PixelFormats.Pbgra32, null),
+        [ChunkResolution.Eighth] = new WriteableBitmap(8, 8, 96, 96, PixelFormats.Pbgra32, null),
+    };
+
+    public Dictionary<ChunkResolution, SKSurface> Surfaces { get; set; } = new();
+    public FolderViewModel StructureRoot { get; }
+    public DocumentTransformViewModel TransformViewModel { get; }
     public int ResizeWidth { get; set; }
     public int ResizeHeight { get; set; }
 
-    private DocumentHelpers Helpers { get; }
 
+    private DocumentHelpers Helpers { get; }
     private ViewModelMain owner;
 
+
+    private bool updateableChangeActive = false;
+
+    private bool selectingRect = false;
+    private bool selectingLasso = false;
+    private bool drawingRectangle = false;
+    private bool drawingPathBasedPen = false;
+    private bool drawingLineBasedPen = false;
+    private bool transformingRectangle = false;
+    private bool shiftingLayer = false;
+
+    private bool transformingSelectionPath = false;
+    ShapeCorners initialSelectionCorners = new();
+
+    private bool pastingImage = false;
+    private Surface? pastedImage;
+
+    private ShapeCorners lastShape = new ShapeCorners();
+    private ShapeData lastShapeData = new();
+
     public DocumentViewModel(ViewModelMain owner)
     {
         this.owner = owner;
@@ -103,7 +163,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
         TransformViewModel.TransformMoved += OnTransformUpdate;
 
         Helpers = new DocumentHelpers(this);
-        StructureRoot = new FolderViewModel(this, Helpers, Helpers.Tracker.Document.StructureRoot);
+        StructureRoot = new FolderViewModel(this, Helpers, Helpers.Tracker.Document.StructureRoot.GuidValue);
 
         UndoCommand = new RelayCommand(Undo);
         RedoCommand = new RelayCommand(Redo);
@@ -141,16 +201,16 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     private void TransformSelectedArea(object? obj)
     {
-        if (updateableChangeActive || SelectedStructureMember is not LayerViewModel layer || SelectionPath.IsEmpty)
+        if (updateableChangeActive || SelectedStructureMember is not LayerViewModel layer || SelectionPathBindable.IsEmpty)
             return;
         IReadOnlyChunkyImage? layerImage = (Helpers.Tracker.Document.FindMember(layer.GuidValue) as IReadOnlyLayer)?.LayerImage;
         if (layerImage is null)
             return;
 
         // find area location and size
-        using SKPath path = SelectionPath;
+        using SKPath path = new(SelectionPathBindable);
         var bounds = (RectD)path.TightBounds;
-        bounds = bounds.Intersect(new RectD(VecD.Zero, new(Width, Height)));
+        bounds = bounds.Intersect(new RectD(VecD.Zero, SizeBindable));
         var intBounds = (RectI)bounds.RoundOutwards();
 
         // extract surface to be transformed
@@ -178,7 +238,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     private void ApplyMask(object? obj)
     {
-        if (updateableChangeActive || SelectedStructureMember is not LayerViewModel layer || !layer.HasMask)
+        if (updateableChangeActive || SelectedStructureMember is not LayerViewModel layer || !layer.HasMaskBindable)
             return;
         Helpers.ActionAccumulator.AddFinishedActions(new ApplyLayerMask_Action(layer.GuidValue));
     }
@@ -187,28 +247,10 @@ internal class DocumentViewModel : INotifyPropertyChanged
     {
         if (updateableChangeActive || SelectedStructureMember is null)
             return;
-        SelectedStructureMember.ClipToMemberBelowEnabled = !SelectedStructureMember.ClipToMemberBelowEnabled;
+        var member = SelectedStructureMember;
+        member.ClipToMemberBelowEnabledBindable = !member.ClipToMemberBelowEnabledBindable;
     }
 
-    private bool updateableChangeActive = false;
-
-    private bool selectingRect = false;
-    private bool selectingLasso = false;
-    private bool drawingRectangle = false;
-    private bool drawingPathBasedPen = false;
-    private bool drawingLineBasedPen = false;
-    private bool transformingRectangle = false;
-    private bool shiftingLayer = false;
-
-    private bool transformingSelectionPath = false;
-    ShapeCorners initialSelectionCorners = new();
-
-    private bool pastingImage = false;
-    private Surface? pastedImage;
-
-    private ShapeCorners lastShape = new ShapeCorners();
-    private ShapeData lastShapeData = new();
-
     private void DragSymmetry(object? obj)
     {
         if (obj is null)
@@ -228,7 +270,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     {
         if (SelectedStructureMember is null)
             return;
-        bool drawOnMask = SelectedStructureMember.HasMask && SelectedStructureMember.ShouldDrawOnMask;
+        bool drawOnMask = SelectedStructureMember.HasMaskBindable && SelectedStructureMember.ShouldDrawOnMask;
         if (SelectedStructureMember is not LayerViewModel && !drawOnMask)
             return;
         updateableChangeActive = true;
@@ -254,7 +296,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     {
         if (SelectedStructureMember is null)
             return;
-        bool drawOnMask = SelectedStructureMember.HasMask && SelectedStructureMember.ShouldDrawOnMask;
+        bool drawOnMask = SelectedStructureMember.HasMaskBindable && SelectedStructureMember.ShouldDrawOnMask;
         if (SelectedStructureMember is not LayerViewModel && !drawOnMask)
             return;
         updateableChangeActive = true;
@@ -280,7 +322,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     {
         if (SelectedStructureMember is null)
             return;
-        bool drawOnMask = SelectedStructureMember.HasMask && SelectedStructureMember.ShouldDrawOnMask;
+        bool drawOnMask = SelectedStructureMember.HasMaskBindable && SelectedStructureMember.ShouldDrawOnMask;
         if (SelectedStructureMember is not LayerViewModel && !drawOnMask)
             return;
         updateableChangeActive = true;
@@ -320,7 +362,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     private void TransformSelectionPath(object? arg)
     {
-        var path = SelectionPath;
+        var path = SelectionPathBindable;
         if (path.IsEmpty)
             return;
         updateableChangeActive = true;
@@ -480,7 +522,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     {
         if (updateableChangeActive || SelectedStructureMember is not LayerViewModel layer)
             return;
-        layer.LockTransparency = !layer.LockTransparency;
+        layer.LockTransparencyBindable = !layer.LockTransparencyBindable;
     }
 
     private void ResizeCanvas(object? param)
@@ -497,14 +539,14 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     private void CreateMask(object? param)
     {
-        if (updateableChangeActive || SelectedStructureMember is null || SelectedStructureMember.HasMask)
+        if (updateableChangeActive || SelectedStructureMember is null || SelectedStructureMember.HasMaskBindable)
             return;
         Helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMemberMask_Action(SelectedStructureMember.GuidValue));
     }
 
     private void DeleteMask(object? param)
     {
-        if (updateableChangeActive || SelectedStructureMember is null || !SelectedStructureMember.HasMask)
+        if (updateableChangeActive || SelectedStructureMember is null || !SelectedStructureMember.HasMaskBindable)
             return;
         Helpers.ActionAccumulator.AddFinishedActions(new DeleteStructureMemberMask_Action(SelectedStructureMember.GuidValue));
     }
@@ -525,7 +567,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
         //make a new layer, put combined image onto it, delete layers that were merged
         Helpers.ActionAccumulator.AddActions(
             new CreateStructureMember_Action(parent.GuidValue, newGuid, index, StructureMemberType.Layer),
-            new StructureMemberName_Action(newGuid, child.Name + "-comb"),
+            new StructureMemberName_Action(newGuid, child.NameBindable + "-comb"),
             new CombineStructureMembersOnto_Action(selected.ToHashSet(), newGuid));
         foreach (var member in selected)
             Helpers.ActionAccumulator.AddActions(new DeleteStructureMember_Action(member));
@@ -549,4 +591,9 @@ internal class DocumentViewModel : INotifyPropertyChanged
                 AddSelectedMembers(innerFolder, collection);
         }
     }
+
+    public void RaisePropertyChanged(string name)
+    {
+        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+    }
 }

+ 3 - 7
src/PixiEditorPrototype/ViewModels/FolderViewModel.cs

@@ -1,5 +1,5 @@
-using System.Collections.ObjectModel;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using System;
+using System.Collections.ObjectModel;
 using PixiEditorPrototype.Models;
 
 namespace PixiEditorPrototype.ViewModels;
@@ -7,9 +7,5 @@ namespace PixiEditorPrototype.ViewModels;
 internal class FolderViewModel : StructureMemberViewModel
 {
     public ObservableCollection<StructureMemberViewModel> Children { get; } = new();
-    public FolderViewModel(DocumentViewModel doc, DocumentHelpers helpers, IReadOnlyFolder member) : base(doc, helpers, member)
-    {
-    }
-
-
+    public FolderViewModel(DocumentViewModel doc, DocumentHelpers helpers, Guid guidValue) : base(doc, helpers, guidValue) { }
 }

+ 11 - 5
src/PixiEditorPrototype/ViewModels/LayerViewModel.cs

@@ -1,16 +1,22 @@
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using System;
 using PixiEditorPrototype.Models;
 
 namespace PixiEditorPrototype.ViewModels;
 
 internal class LayerViewModel : StructureMemberViewModel
 {
-    public bool LockTransparency
+    bool lockTransparency;
+    public void SetLockTransparency(bool lockTransparency)
     {
-        get => ((IReadOnlyLayer)member).LockTransparency;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(member.GuidValue, value));
+        this.lockTransparency = lockTransparency;
+        RaisePropertyChanged(nameof(LockTransparencyBindable));
     }
-    public LayerViewModel(DocumentViewModel doc, DocumentHelpers helpers, IReadOnlyLayer layer) : base(doc, helpers, layer)
+    public bool LockTransparencyBindable
+    {
+        get => lockTransparency;
+        set => Helpers.ActionAccumulator.AddFinishedActions(new LayerLockTransparency_Action(GuidValue, value));
+    }
+    public LayerViewModel(DocumentViewModel doc, DocumentHelpers helpers, Guid guidValue) : base(doc, helpers, guidValue)
     {
     }
 }

+ 79 - 33
src/PixiEditorPrototype/ViewModels/StructureMemberViewModel.cs

@@ -3,7 +3,6 @@ using System.ComponentModel;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditorPrototype.Models;
 using SkiaSharp;
@@ -12,50 +11,102 @@ namespace PixiEditorPrototype.ViewModels;
 
 internal abstract class StructureMemberViewModel : INotifyPropertyChanged
 {
-    protected IReadOnlyStructureMember member;
-
     public event PropertyChangedEventHandler? PropertyChanged;
     protected DocumentViewModel Document { get; }
     protected DocumentHelpers Helpers { get; }
 
-    public string Name
+
+    private string name = "";
+    public void SetName(string name)
+    {
+        this.name = name;
+        RaisePropertyChanged(nameof(NameBindable));
+    }
+    public string NameBindable
     {
-        get => member.Name;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(member.GuidValue, value));
+        get => name;
+        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberName_Action(GuidValue, value));
     }
 
-    public bool IsVisible
+    private bool isVisible;
+    public void SetIsVisible(bool isVisible)
     {
-        get => member.IsVisible;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberIsVisible_Action(value, member.GuidValue));
+        this.isVisible = isVisible;
+        RaisePropertyChanged(nameof(IsVisibleBindable));
+    }
+    public bool IsVisibleBindable
+    {
+        get => isVisible;
+        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberIsVisible_Action(value, GuidValue));
     }
 
-    public bool MaskIsVisible
+    private bool maskIsVisible;
+    public void SetMaskIsVisible(bool maskIsVisible)
+    {
+        this.maskIsVisible = maskIsVisible;
+        RaisePropertyChanged(nameof(MaskIsVisibleBindable));
+    }
+    public bool MaskIsVisibleBindable
     {
-        get => member.MaskIsVisible;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberMaskIsVisible_Action(value, member.GuidValue));
+        get => maskIsVisible;
+        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberMaskIsVisible_Action(value, GuidValue));
     }
 
-    public BlendMode BlendMode
+    private BlendMode blendMode;
+    public void SetBlendMode(BlendMode blendMode)
     {
-        get => member.BlendMode;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberBlendMode_Action(value, member.GuidValue));
+        this.blendMode = blendMode;
+        RaisePropertyChanged(nameof(BlendModeBindable));
+    }
+    public BlendMode BlendModeBindable
+    {
+        get => blendMode;
+        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberBlendMode_Action(value, GuidValue));
     }
 
-    public bool ClipToMemberBelowEnabled
+    private bool clipToMemberBelowEnabled;
+    public void SetClipToMemberBelowEnabled(bool clipToMemberBelowEnabled)
+    {
+        this.clipToMemberBelowEnabled = clipToMemberBelowEnabled;
+        RaisePropertyChanged(nameof(ClipToMemberBelowEnabledBindable));
+    }
+    public bool ClipToMemberBelowEnabledBindable
     {
-        get => member.ClipToMemberBelow;
-        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberClipToMemberBelow_Action(value, member.GuidValue));
+        get => clipToMemberBelowEnabled;
+        set => Helpers.ActionAccumulator.AddFinishedActions(new StructureMemberClipToMemberBelow_Action(value, GuidValue));
     }
 
-    public bool IsSelected { get; set; }
-    public bool ShouldDrawOnMask { get; set; }
+    private bool hasMask;
+    public void SetHasMask(bool hasMask)
+    {
+        this.hasMask = hasMask;
+        RaisePropertyChanged(nameof(HasMaskBindable));
+    }
+    public bool HasMaskBindable
+    {
+        get => hasMask;
+    }
 
-    public float Opacity => member.Opacity;
+    private Guid guidValue;
+    public Guid GuidValue
+    {
+        get => guidValue;
+    }
 
-    public Guid GuidValue => member.GuidValue;
+    private float opacity;
+    public void SetOpacity(float opacity)
+    {
+        this.opacity = opacity;
+        RaisePropertyChanged(nameof(OpacityBindable));
+    }
+    public float OpacityBindable
+    {
+        get => opacity;
+    }
+
+    public bool IsSelected { get; set; }
+    public bool ShouldDrawOnMask { get; set; }
 
-    public bool HasMask => member.Mask is not null;
 
     public const int PreviewSize = 48;
     public WriteableBitmap PreviewBitmap { get; set; }
@@ -63,11 +114,10 @@ internal abstract class StructureMemberViewModel : INotifyPropertyChanged
 
     public WriteableBitmap? MaskPreviewBitmap { get; set; }
     public SKSurface? MaskPreviewSurface { get; set; }
+
     public RelayCommand MoveUpCommand { get; }
     public RelayCommand MoveDownCommand { get; }
-
     public RelayCommand UpdateOpacityCommand { get; }
-
     public RelayCommand EndOpacityUpdateCommand { get; }
 
     public void RaisePropertyChanged(string name)
@@ -84,24 +134,20 @@ internal abstract class StructureMemberViewModel : INotifyPropertyChanged
             new VecI(prSize, (int)Math.Round(prSize * proportions));
     }
 
-    public StructureMemberViewModel(DocumentViewModel doc, DocumentHelpers helpers, IReadOnlyStructureMember member)
+    public StructureMemberViewModel(DocumentViewModel doc, DocumentHelpers helpers, Guid guidValue)
     {
-        this.member = member;
         Document = doc;
         Helpers = helpers;
+
         MoveUpCommand = new(_ => Helpers.StructureHelper.MoveStructureMember(GuidValue, false));
         MoveDownCommand = new(_ => Helpers.StructureHelper.MoveStructureMember(GuidValue, true));
         UpdateOpacityCommand = new(UpdateOpacity);
         EndOpacityUpdateCommand = new(EndOpacityUpdate);
 
-        var previewSize = CalculatePreviewSize(new(doc.Width, doc.Height));
+        this.guidValue = guidValue;
+        var previewSize = CalculatePreviewSize(doc.SizeBindable);
         PreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
         PreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
-        if (member.Mask is not null)
-        {
-            MaskPreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
-            MaskPreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
-        }
     }
 
     private void EndOpacityUpdate(object? opacity)

+ 24 - 24
src/PixiEditorPrototype/Views/MainWindow.xaml

@@ -39,7 +39,7 @@
                     <Button Margin="5,0" Command="{Binding ActiveDocument.ApplyMaskCommand}" Width="80">Apply Mask</Button>
                 </StackPanel>
                 <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,5,0,0">
-                    <controls:BlendModeComboBox Margin="5,0" Width="80" SelectedBlendMode="{Binding ActiveDocument.SelectedStructureMember.BlendMode, Mode=TwoWay}"/>
+                    <controls:BlendModeComboBox Margin="5,0" Width="80" SelectedBlendMode="{Binding ActiveDocument.SelectedStructureMember.BlendModeBindable, Mode=TwoWay}"/>
                 </StackPanel>
                 <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,5,0,0">
                     <Button Margin="5,0" Command="{Binding ActiveDocument.ToggleLockTransparencyCommand}" Width="80">Lock Alpha</Button>
@@ -48,11 +48,11 @@
                 </StackPanel>
                 <DockPanel DockPanel.Dock="Top" HorizontalAlignment="Stretch" Margin="0,5,0,5">
                     <Button Width="80" Margin="5,0" Command="{Binding ActiveDocument.CombineCommand}">Merge</Button>
-                    <TextBlock Text="{Binding ActiveDocument.SelectedStructureMember.Opacity, StringFormat=N2}" 
+                    <TextBlock Text="{Binding ActiveDocument.SelectedStructureMember.OpacityBindable, StringFormat=N2}" 
                            Margin="5,0" DockPanel.Dock="Right" VerticalAlignment="Center" TextAlignment="Center" d:Text="1.00" Width="30"/>
                     <Slider Minimum="0" Maximum="1" SmallChange="0.01" LargeChange="0.1" IsSnapToTickEnabled="True" TickFrequency="0.01" x:Name="opacitySlider"
                             VerticalAlignment="Center" HorizontalAlignment="Stretch"
-                            Value="{Binding ActiveDocument.SelectedStructureMember.Opacity, Mode=OneWay}">
+                            Value="{Binding ActiveDocument.SelectedStructureMember.OpacityBindable, Mode=OneWay}">
                         <i:Interaction.Behaviors>
                             <beh:SliderUpdateBehavior
                                 DragValueChanged="{Binding ActiveDocument.SelectedStructureMember.UpdateOpacityCommand}"
@@ -87,14 +87,14 @@
                         <HierarchicalDataTemplate DataType="{x:Type vm:FolderViewModel}" ItemsSource="{Binding Children}">
                             <StackPanel Orientation="Horizontal" MinWidth="200" Background="Wheat">
                                 <StackPanel Orientation="Vertical" VerticalAlignment="Center">
-                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsVisible}"/>
-                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding MaskIsVisible}"
-                                              Visibility="{Binding HasMask, Converter={StaticResource BoolToVisibilityConverter}}"
+                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsVisibleBindable}"/>
+                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding MaskIsVisibleBindable}"
+                                              Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
                                               Background="LightBlue"/>
                                 </StackPanel>
                                 <Rectangle 
                                     Fill="DarkRed" Width="8" Margin="3,0" 
-                                    Visibility="{Binding ClipToMemberBelowEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
+                                    Visibility="{Binding ClipToMemberBelowEnabledBindable, Converter={StaticResource BoolToVisibilityConverter}}"/>
                                 <StackPanel>
                                     <Button Width="12" Command="{Binding MoveUpCommand}">^</Button>
                                     <Button Width="12" Command="{Binding MoveDownCommand}">v</Button>
@@ -105,21 +105,21 @@
                                     <Image Source="{Binding PreviewBitmap}"></Image>
                                 </Border>
                                 <Border 
-                                    Visibility="{Binding HasMask, Converter={StaticResource BoolToVisibilityConverter}}"
+                                    Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
                                     BorderBrush="Black" BorderThickness="1" Background="White" MaxWidth="30" MaxHeight="30" 
                                     HorizontalAlignment="Center" VerticalAlignment="Center" Margin="3,0,0,0">
                                     <Image Source="{Binding MaskPreviewBitmap}"></Image>
                                 </Border>
                                 <StackPanel VerticalAlignment="Center">
                                     <DockPanel Margin="3, 0, 0, 0">
-                                        <TextBlock Text="{Binding Opacity}" Width="25"/>
-                                        <TextBlock Text="{Binding BlendMode, Converter={StaticResource BlendModeToStringConverter}}"/>
+                                        <TextBlock Text="{Binding OpacityBindable}" Width="25"/>
+                                        <TextBlock Text="{Binding BlendModeBindable, Converter={StaticResource BlendModeToStringConverter}}"/>
                                     </DockPanel>
-                                    <TextBox HorizontalAlignment="Left" Width="65" Text="{Binding Name}" Margin="3, 0, 0, 0" Height="20"/>
+                                    <TextBox HorizontalAlignment="Left" Width="65" Text="{Binding NameBindable}" Margin="3, 0, 0, 0" Height="20"/>
                                 </StackPanel>
                                 <StackPanel VerticalAlignment="Center" Margin="3, 0, 0, 0">
                                     <CheckBox VerticalAlignment="Center"
-                                          Visibility="{Binding HasMask, Converter={StaticResource BoolToVisibilityConverter}}"
+                                          Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
                                           IsChecked="{Binding ShouldDrawOnMask}">Edit Mask</CheckBox>
                                     <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsSelected}">Select</CheckBox>
                                 </StackPanel>
@@ -131,14 +131,14 @@
                         <DataTemplate DataType="{x:Type vm:LayerViewModel}">
                             <StackPanel Orientation="Horizontal">
                                 <StackPanel Orientation="Vertical" VerticalAlignment="Center">
-                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsVisible}"/>
-                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding MaskIsVisible}"
-                                              Visibility="{Binding HasMask, Converter={StaticResource BoolToVisibilityConverter}}"
+                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding IsVisibleBindable}"/>
+                                    <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding MaskIsVisibleBindable}"
+                                              Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
                                               Background="LightBlue"/>
                                 </StackPanel>
                                 <Rectangle 
                                     Fill="DarkRed" Width="8" Margin="3,0" 
-                                    Visibility="{Binding ClipToMemberBelowEnabled, Converter={StaticResource BoolToVisibilityConverter}}"/>
+                                    Visibility="{Binding ClipToMemberBelowEnabledBindable, Converter={StaticResource BoolToVisibilityConverter}}"/>
                                 <StackPanel>
                                     <Button Width="12" Command="{Binding MoveUpCommand}">^</Button>
                                     <Button Width="12" Command="{Binding MoveDownCommand}">v</Button>
@@ -149,26 +149,26 @@
                                     <Image Source="{Binding PreviewBitmap}"></Image>
                                 </Border>
                                 <Border 
-                                    Visibility="{Binding HasMask, Converter={StaticResource BoolToVisibilityConverter}}"
+                                    Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
                                     BorderBrush="Black" BorderThickness="1" Background="White" MaxWidth="30" MaxHeight="30" 
                                     HorizontalAlignment="Center" VerticalAlignment="Center" Margin="3,0,0,0">
                                     <Image Source="{Binding MaskPreviewBitmap}"></Image>
                                 </Border>
                                 <StackPanel VerticalAlignment="Center">
                                     <DockPanel Margin="3, 0, 0, 0">
-                                        <TextBlock Text="{Binding Opacity}" Width="25"/>
-                                        <TextBlock Text="{Binding BlendMode, Converter={StaticResource BlendModeToStringConverter}}"/>
+                                        <TextBlock Text="{Binding OpacityBindable}" Width="25"/>
+                                        <TextBlock Text="{Binding BlendModeBindable, Converter={StaticResource BlendModeToStringConverter}}"/>
                                     </DockPanel>
-                                    <TextBox HorizontalAlignment="Left" Width="65" Text="{Binding Name}" Margin="3, 0, 0, 0" Height="20"/>
+                                    <TextBox HorizontalAlignment="Left" Width="65" Text="{Binding NameBindable}" Margin="3, 0, 0, 0" Height="20"/>
                                 </StackPanel>
                                 <StackPanel VerticalAlignment="Center" Margin="3, 0, 0, 0">
                                     <CheckBox VerticalAlignment="Center"
-                                          Visibility="{Binding HasMask, Converter={StaticResource BoolToVisibilityConverter}}"
+                                          Visibility="{Binding HasMaskBindable, Converter={StaticResource BoolToVisibilityConverter}}"
                                           IsChecked="{Binding ShouldDrawOnMask}">Edit Mask</CheckBox>
                                     <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsSelected}">Select</CheckBox>
                                 </StackPanel>
                                 <StackPanel Margin="3">
-                                    <TextBlock Visibility="{Binding LockTransparency, Converter={StaticResource BoolToVisibilityConverter}}">🙾</TextBlock>
+                                    <TextBlock Visibility="{Binding LockTransparencyBindable, Converter={StaticResource BoolToVisibilityConverter}}">🙾</TextBlock>
                                     <TextBlock Visibility="Collapsed">🔒</TextBlock>
                                 </StackPanel>
                             </StackPanel>
@@ -229,10 +229,10 @@
                     IsChecked="{Binding KeepOriginalImageOnTransform}">Keep area</CheckBox>
                 <CheckBox 
                     x:Name="horizontalSymmetryCheckbox" Margin="5,0" 
-                    IsChecked="{Binding ActiveDocument.HorizontalSymmetryAxisEnabled}">Hor Sym</CheckBox>
+                    IsChecked="{Binding ActiveDocument.HorizontalSymmetryAxisEnabledBindable}">Hor Sym</CheckBox>
                 <CheckBox 
                     x:Name="verticalSymmetryCheckbox" Margin="5,0" 
-                    IsChecked="{Binding ActiveDocument.VerticalSymmetryAxisEnabled}">Ver Sym</CheckBox>
+                    IsChecked="{Binding ActiveDocument.VerticalSymmetryAxisEnabledBindable}">Ver Sym</CheckBox>
             </StackPanel>
         </Border>
         <Grid>