Browse Source

Merge pull request #297 from PixiEditor/quickfill

0.1.6.6 dev release
Krzysztof Krysiński 3 years ago
parent
commit
067bb647e2
47 changed files with 882 additions and 410 deletions
  1. 45 0
      PixiEditor/Helpers/ClipboardHelper.cs
  2. 19 0
      PixiEditor/Helpers/Converters/DebugConverter.cs
  3. 0 34
      PixiEditor/Helpers/Converters/DockingManagerActiveContentConverter.cs
  4. 4 6
      PixiEditor/Helpers/Converters/LayersToStructuredLayersConverter.cs
  5. 4 4
      PixiEditor/Helpers/Extensions/ParserHelpers.cs
  6. 21 1
      PixiEditor/Models/Controllers/BitmapManager.cs
  7. 2 4
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  8. 70 36
      PixiEditor/Models/Controllers/ClipboardController.cs
  9. 26 2
      PixiEditor/Models/Controllers/ToolSession.cs
  10. 11 5
      PixiEditor/Models/Controllers/UndoManager.cs
  11. 1 1
      PixiEditor/Models/DataHolders/Document/Document.Constructors.cs
  12. 41 41
      PixiEditor/Models/DataHolders/Document/Document.Layers.cs
  13. 4 1
      PixiEditor/Models/DataHolders/Surface.cs
  14. 12 7
      PixiEditor/Models/IO/Exporter.cs
  15. 33 3
      PixiEditor/Models/ImageManipulation/BitmapUtils.cs
  16. 5 5
      PixiEditor/Models/Layers/BasicLayer.cs
  17. 9 0
      PixiEditor/Models/Layers/IHasGuid.cs
  18. 8 8
      PixiEditor/Models/Layers/Layer.cs
  19. 21 16
      PixiEditor/Models/Layers/LayerGroup.cs
  20. 8 15
      PixiEditor/Models/Layers/LayerHelper.cs
  21. 18 18
      PixiEditor/Models/Layers/LayerStructure.cs
  22. 17 18
      PixiEditor/Models/Layers/StructuredLayerTree.cs
  23. 2 2
      PixiEditor/Models/Layers/Utils/LayerStructureUtils.cs
  24. 38 5
      PixiEditor/Models/Tools/BitmapOperationTool.cs
  25. 6 1
      PixiEditor/Models/Tools/Tool.cs
  26. 1 0
      PixiEditor/Models/Tools/Tools/FloodFillTool.cs
  27. 4 42
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  28. 2 1
      PixiEditor/Models/Tools/Tools/PenTool.cs
  29. 3 2
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  30. 17 0
      PixiEditor/Models/Undo/LayerChunk.cs
  31. 145 36
      PixiEditor/Models/Undo/StorageBasedChange.cs
  32. 7 2
      PixiEditor/Models/Undo/UndoLayer.cs
  33. 2 2
      PixiEditor/Properties/AssemblyInfo.cs
  34. 8 8
      PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  35. 16 7
      PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
  36. 6 1
      PixiEditor/ViewModels/ViewModelMain.cs
  37. 1 2
      PixiEditor/Views/MainWindow.xaml
  38. 1 2
      PixiEditor/Views/MainWindow.xaml.cs
  39. 4 4
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml
  40. 7 7
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs
  41. 1 1
      PixiEditor/Views/UserControls/Layers/LayerItem.xaml.cs
  42. 1 1
      PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml
  43. 2 2
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml
  44. 185 13
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs
  45. 1 1
      PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml
  46. 2 2
      PixiEditorTests/ModelsTests/DataHoldersTests/DocumentLayersTests.cs
  47. 41 41
      PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs

+ 45 - 0
PixiEditor/Helpers/ClipboardHelper.cs

@@ -0,0 +1,45 @@
+using System.Windows;
+
+namespace PixiEditor.Helpers
+{
+    class ClipboardHelper
+    {
+        public static bool TrySetDataObject(DataObject obj, bool copy)
+        {
+            try
+            {
+                Clipboard.SetDataObject(obj, copy);
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        public static DataObject TryGetDataObject()
+        {
+            try
+            {
+                return (DataObject)Clipboard.GetDataObject();
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        public static bool TryClear()
+        {
+            try
+            {
+                Clipboard.Clear();
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+    }
+}

+ 19 - 0
PixiEditor/Helpers/Converters/DebugConverter.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+
+namespace PixiEditor.Helpers.Converters
+{
+    public class DebugConverter
+        : SingleInstanceConverter<DebugConverter>
+    {
+        public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value;
+        }
+
+        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return value;
+        }
+    }
+}

+ 0 - 34
PixiEditor/Helpers/Converters/DockingManagerActiveContentConverter.cs

@@ -1,34 +0,0 @@
-using PixiEditor.Models.DataHolders;
-using System;
-using System.Globalization;
-using System.Windows;
-using System.Windows.Data;
-
-namespace PixiEditor.Helpers.Converters
-{
-    class DockingManagerActiveContentConverter : IValueConverter
-    {
-        private Document cachedDocument = null;
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (value == null)
-                return DependencyProperty.UnsetValue;
-            if (value is Document document)
-            {
-                cachedDocument = document;
-                return document;
-            }
-            return DependencyProperty.UnsetValue;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (value is Document document)
-                return document;
-            if (value != null && cachedDocument != null && !cachedDocument.Disposed)
-                return cachedDocument;
-            cachedDocument = null;
-            return DependencyProperty.UnsetValue;
-        }
-    }
-}

+ 4 - 6
PixiEditor/Helpers/Converters/LayersToStructuredLayersConverter.cs

@@ -1,12 +1,10 @@
-using PixiEditor.Models.Layers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Globalization;
 using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
-using System.Windows.Data;
-using PixiEditor.Models.DataHolders;
 
 
 namespace PixiEditor.Helpers.Converters
 namespace PixiEditor.Helpers.Converters
 {
 {
@@ -35,7 +33,7 @@ namespace PixiEditor.Helpers.Converters
                 {
                 {
                     cachedTree = new StructuredLayerTree(layers, structure);
                     cachedTree = new StructuredLayerTree(layers, structure);
                     lastLayers = layers;
                     lastLayers = layers;
-                    lastLayerGuids = layers.Select(x => x.LayerGuid).ToList();
+                    lastLayerGuids = layers.Select(x => x.GuidValue).ToList();
                     lastStructure = structure.CloneGroups();
                     lastStructure = structure.CloneGroups();
                 }
                 }
 
 
@@ -52,7 +50,7 @@ namespace PixiEditor.Helpers.Converters
 
 
         private bool LayerOrderIsDifferent(IList<Layer> layers)
         private bool LayerOrderIsDifferent(IList<Layer> layers)
         {
         {
-            var guids = layers.Select(x => x.LayerGuid).ToArray();
+            var guids = layers.Select(x => x.GuidValue).ToArray();
             return !guids.SequenceEqual(lastLayerGuids);
             return !guids.SequenceEqual(lastLayerGuids);
         }
         }
 
 

+ 4 - 4
PixiEditor/Helpers/Extensions/ParserHelpers.cs

@@ -75,8 +75,8 @@ namespace PixiEditor.Helpers.Extensions
                 Opacity = sgroup.Opacity,
                 Opacity = sgroup.Opacity,
                 IsVisible = sgroup.IsVisible,
                 IsVisible = sgroup.IsVisible,
                 Parent = parent,
                 Parent = parent,
-                StartLayerGuid = document.Layers[sgroup.StartLayer].LayerGuid,
-                EndLayerGuid = document.Layers[sgroup.EndLayer].LayerGuid
+                StartLayerGuid = document.Layers[sgroup.StartLayer].GuidValue,
+                EndLayerGuid = document.Layers[sgroup.EndLayer].GuidValue
             };
             };
 
 
             group.Subgroups = new(sgroup.Subgroups.ToGroups(document, group));
             group.Subgroups = new(sgroup.Subgroups.ToGroups(document, group));
@@ -127,12 +127,12 @@ namespace PixiEditor.Helpers.Extensions
 
 
             for (int i = 0; i < document.Layers.Count; i++)
             for (int i = 0; i < document.Layers.Count; i++)
             {
             {
-                if (group.StartLayerGuid == document.Layers[i].LayerGuid)
+                if (group.StartLayerGuid == document.Layers[i].GuidValue)
                 {
                 {
                     serializable.StartLayer = i;
                     serializable.StartLayer = i;
                 }
                 }
 
 
-                if (group.EndLayerGuid == document.Layers[i].LayerGuid)
+                if (group.EndLayerGuid == document.Layers[i].GuidValue)
                 {
                 {
                     serializable.EndLayer = i;
                     serializable.EndLayer = i;
                 }
                 }

+ 21 - 1
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -29,14 +29,32 @@ namespace PixiEditor.Models.Controllers
             get => activeDocument;
             get => activeDocument;
             set
             set
             {
             {
+                if (activeDocument == value)
+                    return;
                 activeDocument?.UpdatePreviewImage();
                 activeDocument?.UpdatePreviewImage();
                 Document oldDoc = activeDocument;
                 Document oldDoc = activeDocument;
                 activeDocument = value;
                 activeDocument = value;
                 RaisePropertyChanged(nameof(ActiveDocument));
                 RaisePropertyChanged(nameof(ActiveDocument));
+                ActiveWindow = value;
                 DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
                 DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
             }
             }
         }
         }
 
 
+        private object activeWindow;
+        public object ActiveWindow
+        {
+            get => activeWindow;
+            set
+            {
+                if (activeWindow == value)
+                    return;
+                activeWindow = value;
+                RaisePropertyChanged(nameof(ActiveWindow));
+                if (activeWindow is Document doc)
+                    ActiveDocument = doc;
+            }
+        }
+
         public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
         public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
         public event EventHandler StopUsingTool;
         public event EventHandler StopUsingTool;
 
 
@@ -68,7 +86,7 @@ namespace PixiEditor.Models.Controllers
         private ToolSession activeSession = null;
         private ToolSession activeSession = null;
 
 
 
 
-        public BitmapManager(ToolsViewModel tools)
+        public BitmapManager(ToolsViewModel tools, UndoViewModel undo)
         {
         {
             _tools = tools;
             _tools = tools;
 
 
@@ -80,6 +98,8 @@ namespace PixiEditor.Models.Controllers
             ToolSessionController.KeyStateChanged += (_, _) => UpdateActionDisplay(_tools.ActiveTool);
             ToolSessionController.KeyStateChanged += (_, _) => UpdateActionDisplay(_tools.ActiveTool);
             BitmapOperations = new BitmapOperationsUtility(this, tools);
             BitmapOperations = new BitmapOperationsUtility(this, tools);
 
 
+            undo.UndoRedoCalled += (_, _) => ToolSessionController.ForceStopActiveSessionIfAny();
+
             DocumentChanged += BitmapManager_DocumentChanged;
             DocumentChanged += BitmapManager_DocumentChanged;
 
 
             _highlightPen = new PenTool(this)
             _highlightPen = new PenTool(this)

+ 2 - 4
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -19,8 +19,6 @@ namespace PixiEditor.Models.Controllers
 
 
         public ToolsViewModel Tools { get; set; }
         public ToolsViewModel Tools { get; set; }
 
 
-        private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
-
         public BitmapOperationsUtility(BitmapManager manager, ToolsViewModel tools)
         public BitmapOperationsUtility(BitmapManager manager, ToolsViewModel tools)
         {
         {
             Manager = manager;
             Manager = manager;
@@ -39,7 +37,7 @@ namespace PixiEditor.Models.Controllers
             BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
             BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
             for (int i = 0; i < layers.Length; i++)
             for (int i = 0; i < layers.Length; i++)
             {
             {
-                Guid guid = layers[i].LayerGuid;
+                Guid guid = layers[i].GuidValue;
                 layers[i].SetPixels(changes);
                 layers[i].SetPixels(changes);
             }
             }
 
 
@@ -95,7 +93,7 @@ namespace PixiEditor.Models.Controllers
                     activeLayer.LayerBitmap.SkiaSurface.Canvas,
                     activeLayer.LayerBitmap.SkiaSurface.Canvas,
                     previewLayer.OffsetX - activeLayer.OffsetX,
                     previewLayer.OffsetX - activeLayer.OffsetX,
                     previewLayer.OffsetY - activeLayer.OffsetY,
                     previewLayer.OffsetY - activeLayer.OffsetY,
-                    BlendingPaint
+                    Surface.BlendingPaint
                 );
                 );
 
 
             Manager.ActiveLayer.InvokeLayerBitmapChange(dirtyRect);
             Manager.ActiveLayer.InvokeLayerBitmapChange(dirtyRect);

+ 70 - 36
PixiEditor/Models/Controllers/ClipboardController.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Exceptions;
 using PixiEditor.Exceptions;
+using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.ImageManipulation;
@@ -27,21 +28,54 @@ namespace PixiEditor.Models.Controllers
                     "PixiEditor",
                     "PixiEditor",
                     "Copied.png");
                     "Copied.png");
 
 
+        /// <summary>
+        /// Copies the selection to clipboard in PNG, Bitmap and DIB formats. <para/>
+        /// Also serailizes the <paramref name="document"/> in the PIXI format and copies it to the clipboard.
+        /// </summary>
+        public static void CopyToClipboard(Document document)
+        {
+            CopyToClipboard(
+                document.Layers.Where(x => document.GetFinalLayerIsVisible(x) && x.IsActive).ToArray(),
+                document.ActiveSelection.SelectionLayer,
+                document.LayerStructure,
+                document.Width,
+                document.Height,
+                null/*document.ToSerializable()*/);
+        }
+
+        private static Surface CreateMaskedCombinedSurface(Layer[] layers, LayerStructure structure, Layer selLayer)
+        {
+            if (layers.Length == 0)
+                throw new ArgumentException("Can't combine 0 layers");
+            selLayer.ClipCanvas();
+
+            Surface combined = BitmapUtils.CombineLayers(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height), layers, structure);
+            using SKImage snapshot = selLayer.LayerBitmap.SkiaSurface.Snapshot();
+            combined.SkiaSurface.Canvas.DrawImage(snapshot, 0, 0, Surface.MaskingPaint);
+            return combined;
+        }
+
         /// <summary>
         /// <summary>
         ///     Copies the selection to clipboard in PNG, Bitmap and DIB formats.
         ///     Copies the selection to clipboard in PNG, Bitmap and DIB formats.
         /// </summary>
         /// </summary>
         /// <param name="layers">Layers where selection is.</param>
         /// <param name="layers">Layers where selection is.</param>
-        public static void CopyToClipboard(Layer[] layers, Coordinates[] selection, int originalImageWidth, int originalImageHeight, SerializableDocument document = null)
+        public static void CopyToClipboard(Layer[] layers, Layer selLayer, LayerStructure structure, int originalImageWidth, int originalImageHeight, SerializableDocument document = null)
         {
         {
-            Clipboard.Clear();
-            using Surface surface = BitmapUtils.CombineLayers(new Int32Rect(0, 0, originalImageWidth, originalImageHeight), layers);
+            if (!ClipboardHelper.TryClear())
+                return;
+            if (layers.Length == 0)
+                return;
+
+            using Surface surface = CreateMaskedCombinedSurface(layers, structure, selLayer);
             DataObject data = new DataObject();
             DataObject data = new DataObject();
 
 
-            WriteableBitmap combinedBitmaps = surface.ToWriteableBitmap();
-            BitmapSource croppedBmp = BitmapSelectionToBmpSource(combinedBitmaps, selection, out int offsetX, out int offsetY, out int width, out int height);
-            data.SetData(typeof(CropData), new CropData(width, height, offsetX, offsetY).ToStream());
 
 
-            using (SKData pngData = surface.SkiaSurface.Snapshot(SKRectI.Create(offsetX, offsetY, width, height)).Encode())
+            //BitmapSource croppedBmp = BitmapSelectionToBmpSource(finalBitmap, selLayer, out int offsetX, out int offsetY, out int width, out int height);
+
+            //Remove for now
+            //data.SetData(typeof(CropData), new CropData(width, height, offsetX, offsetY).ToStream());
+
+            using (SKData pngData = surface.SkiaSurface.Snapshot().Encode())
             {
             {
                 // Stream should not be disposed
                 // Stream should not be disposed
                 MemoryStream pngStream = new MemoryStream();
                 MemoryStream pngStream = new MemoryStream();
@@ -56,33 +90,22 @@ namespace PixiEditor.Models.Controllers
                 data.SetFileDropList(new StringCollection() { TempCopyFilePath });
                 data.SetFileDropList(new StringCollection() { TempCopyFilePath });
             }
             }
 
 
-            data.SetData(DataFormats.Bitmap, croppedBmp, true); // Bitmap, no transparency
-            data.SetImage(croppedBmp); // DIB format, no transparency
+            WriteableBitmap finalBitmap = surface.ToWriteableBitmap();
+            data.SetData(DataFormats.Bitmap, finalBitmap, true); // Bitmap, no transparency
+            data.SetImage(finalBitmap); // DIB format, no transparency
 
 
+            // Remove pixi copying for now
+            /*
             if (document != null)
             if (document != null)
             {
             {
                 MemoryStream memoryStream = new();
                 MemoryStream memoryStream = new();
                 PixiParser.Serialize(document, memoryStream);
                 PixiParser.Serialize(document, memoryStream);
                 data.SetData("PIXI", memoryStream); // PIXI, supports transparency, layers, groups and swatches
                 data.SetData("PIXI", memoryStream); // PIXI, supports transparency, layers, groups and swatches
-                Clipboard.SetDataObject(data, true);
+                ClipboardHelper.TrySetDataObject(data, true);
             }
             }
+            */
 
 
-            Clipboard.SetDataObject(data, true);
-        }
-
-        /// <summary>
-        /// Copies the selection to clipboard in PNG, Bitmap and DIB formats. <para/>
-        /// Also serailizes the <paramref name="document"/> in the PIXI format and copies it to the clipboard.
-        /// </summary>
-        public static void CopyToClipboard(Document document)
-        {
-            CopyToClipboard(
-                document.Layers.Where(x => document.GetFinalLayerIsVisible(x)).ToArray(),
-                document.ActiveSelection.SelectedPoints.ToArray(),
-                //new Coordinates[] { (0, 0), (15, 15) },
-                document.Width,
-                document.Height,
-                document.ToSerializable());
+            ClipboardHelper.TrySetDataObject(data, true);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -90,7 +113,15 @@ namespace PixiEditor.Models.Controllers
         /// </summary>
         /// </summary>
         public static void PasteFromClipboard()
         public static void PasteFromClipboard()
         {
         {
-            var layers = GetLayersFromClipboard();
+            IEnumerable<Layer> layers;
+            try
+            {
+                layers = GetLayersFromClipboard();
+            }
+            catch
+            {
+                return;
+            }
 
 
             Document activeDocument = ViewModelMain.Current.BitmapManager.ActiveDocument;
             Document activeDocument = ViewModelMain.Current.BitmapManager.ActiveDocument;
             int startIndex = activeDocument.Layers.Count;
             int startIndex = activeDocument.Layers.Count;
@@ -108,10 +139,14 @@ namespace PixiEditor.Models.Controllers
         ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
         ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
         /// </summary>
         /// </summary>
         /// <returns>WriteableBitmap.</returns>
         /// <returns>WriteableBitmap.</returns>
-        public static IEnumerable<Layer> GetLayersFromClipboard()
+        private static IEnumerable<Layer> GetLayersFromClipboard()
         {
         {
-            DataObject data = (DataObject)Clipboard.GetDataObject();
+            DataObject data = ClipboardHelper.TryGetDataObject();
+            if (data == null)
+                yield break;
 
 
+            //Remove pixi for now
+            /*
             if (data.GetDataPresent("PIXI"))
             if (data.GetDataPresent("PIXI"))
             {
             {
                 SerializableDocument document = GetSerializable(data, out CropData crop);
                 SerializableDocument document = GetSerializable(data, out CropData crop);
@@ -121,7 +156,7 @@ namespace PixiEditor.Models.Controllers
                 {
                 {
                     SKRectI intersect;
                     SKRectI intersect;
 
 
-                    if (/*layer.OffsetX > crop.OffsetX + crop.Width || layer.OffsetY > crop.OffsetY + crop.Height ||*/
+                    if (//layer.OffsetX > crop.OffsetX + crop.Width || layer.OffsetY > crop.OffsetY + crop.Height ||
                         !sLayer.IsVisible || sLayer.Opacity == 0 ||
                         !sLayer.IsVisible || sLayer.Opacity == 0 ||
                         (intersect = SKRectI.Intersect(cropRect, sLayer.GetRect())) == SKRectI.Empty)
                         (intersect = SKRectI.Intersect(cropRect, sLayer.GetRect())) == SKRectI.Empty)
                     {
                     {
@@ -135,7 +170,8 @@ namespace PixiEditor.Models.Controllers
                     yield return layer;
                     yield return layer;
                 }
                 }
             }
             }
-            else if (TryFromSingleImage(data, out Surface singleImage))
+            else */
+            if (TryFromSingleImage(data, out Surface singleImage))
             {
             {
                 yield return new Layer("Image", singleImage);
                 yield return new Layer("Image", singleImage);
             }
             }
@@ -163,17 +199,15 @@ namespace PixiEditor.Models.Controllers
             }
             }
             else
             else
             {
             {
-                throw new NotImplementedException();
+                yield break;
             }
             }
         }
         }
 
 
         public static bool IsImageInClipboard()
         public static bool IsImageInClipboard()
         {
         {
-            DataObject dao = (DataObject)Clipboard.GetDataObject();
+            DataObject dao = ClipboardHelper.TryGetDataObject();
             if (dao == null)
             if (dao == null)
-            {
                 return false;
                 return false;
-            }
 
 
             var files = dao.GetFileDropList();
             var files = dao.GetFileDropList();
 
 
@@ -193,7 +227,7 @@ namespace PixiEditor.Models.Controllers
                    dao.GetDataPresent("PIXI");
                    dao.GetDataPresent("PIXI");
         }
         }
 
 
-        public static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection, out int offsetX, out int offsetY, out int width, out int height)
+        private static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection, out int offsetX, out int offsetY, out int width, out int height)
         {
         {
             offsetX = selection.Min(min => min.X);
             offsetX = selection.Min(min => min.X);
             offsetY = selection.Min(min => min.Y);
             offsetY = selection.Min(min => min.Y);

+ 26 - 2
PixiEditor/Models/Controllers/ToolSession.cs

@@ -3,6 +3,7 @@ using PixiEditor.Models.Tools;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows.Input;
 using System.Windows.Input;
+using SkiaSharp;
 
 
 namespace PixiEditor.Models.Controllers
 namespace PixiEditor.Models.Controllers
 {
 {
@@ -18,6 +19,11 @@ namespace PixiEditor.Models.Controllers
         public bool IsShiftDown { get; private set; }
         public bool IsShiftDown { get; private set; }
         public bool IsAltDown { get; private set; }
         public bool IsAltDown { get; private set; }
 
 
+        private int _smallestX = int.MaxValue;
+        private int _smallestY = int.MaxValue;
+        private int _biggestX = int.MinValue;
+        private int _biggestY = int.MinValue;
+
         public ToolSession(
         public ToolSession(
             Tool tool,
             Tool tool,
             double mouseXOnCanvas,
             double mouseXOnCanvas,
@@ -30,7 +36,12 @@ namespace PixiEditor.Models.Controllers
 
 
             Tool.Session = this;
             Tool.Session = this;
             InvokeKeyboardEvents(keyboardStates);
             InvokeKeyboardEvents(keyboardStates);
-            mouseMovement.Add(new((int)Math.Floor(mouseXOnCanvas), (int)Math.Floor(mouseYOnCanvas)));
+            int x = (int)Math.Floor(mouseXOnCanvas);
+            int y = (int)Math.Floor(mouseYOnCanvas);
+            mouseMovement.Add(new(x, y));
+
+            UpdateMinMax(x, y);
+
             Tool.BeforeUse();
             Tool.BeforeUse();
         }
         }
 
 
@@ -51,7 +62,11 @@ namespace PixiEditor.Models.Controllers
                 throw new Exception("Session has ended already");
                 throw new Exception("Session has ended already");
             ended = true;
             ended = true;
 
 
-            Tool.AfterUse();
+            Tool.AfterUse(SKRectI.Create(
+                _smallestX,
+                _smallestY,
+                _biggestX - _smallestX + 1,
+                _biggestY - _smallestY + 1));
             InvokeReleaseKeyboardEvents(keyboardStates);
             InvokeReleaseKeyboardEvents(keyboardStates);
             Tool.Session = null;
             Tool.Session = null;
         }
         }
@@ -91,7 +106,16 @@ namespace PixiEditor.Models.Controllers
 
 
         public void OnPixelPositionChange(Coordinates pos)
         public void OnPixelPositionChange(Coordinates pos)
         {
         {
+            UpdateMinMax(pos.X, pos.Y);
             mouseMovement.Add(pos);
             mouseMovement.Add(pos);
         }
         }
+
+        private void UpdateMinMax(int x, int y)
+        {
+            _smallestX = Math.Min(_smallestX, x);
+            _smallestY = Math.Min(_smallestY, y);
+            _biggestX = Math.Max(_biggestX, x);
+            _biggestY = Math.Max(_biggestY, y);
+        }
     }
     }
 }
 }

+ 11 - 5
PixiEditor/Models/Controllers/UndoManager.cs

@@ -1,10 +1,10 @@
+using PixiEditor.Models.Undo;
+using PixiEditor.ViewModels;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
-using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.Controllers
 namespace PixiEditor.Models.Controllers
 {
 {
@@ -54,6 +54,10 @@ namespace PixiEditor.Models.Controllers
             // Clears RedoStack if last move wasn't redo or undo and if redo stack is greater than 0.
             // Clears RedoStack if last move wasn't redo or undo and if redo stack is greater than 0.
             if (lastChangeWasUndo == false && RedoStack.Count > 0)
             if (lastChangeWasUndo == false && RedoStack.Count > 0)
             {
             {
+                foreach (var redo in RedoStack)
+                {
+                    redo.Dispose();
+                }
                 RedoStack.Clear();
                 RedoStack.Clear();
             }
             }
 
 
@@ -119,7 +123,7 @@ namespace PixiEditor.Models.Controllers
         /// </summary>
         /// </summary>
         /// <param name="amount">Amount of changes to squash.</param>
         /// <param name="amount">Amount of changes to squash.</param>
         /// <param name="description">Final change description.</param>
         /// <param name="description">Final change description.</param>
-        public void SquashUndoChanges(int amount, string description)
+        public void SquashUndoChanges(int amount, string description, bool reverseOrderOnRedo = true)
         {
         {
             Change[] changes = new Change[amount];
             Change[] changes = new Change[amount];
             for (int i = 0; i < amount; i++)
             for (int i = 0; i < amount; i++)
@@ -145,7 +149,9 @@ namespace PixiEditor.Models.Controllers
 
 
             Action<object[]> process = (object[] props) =>
             Action<object[]> process = (object[] props) =>
             {
             {
-                foreach (var prop in props.Reverse())
+                var finalProps = reverseOrderOnRedo ? props.Reverse() : props;
+
+                foreach (var prop in finalProps)
                 {
                 {
                     Change change = (Change)prop;
                     Change change = (Change)prop;
                     if (change.Process == null)
                     if (change.Process == null)
@@ -210,4 +216,4 @@ namespace PixiEditor.Models.Controllers
             return new Tuple<PropertyInfo, object>(target.GetType().GetProperty(bits.Last()), target);
             return new Tuple<PropertyInfo, object>(target.GetType().GetProperty(bits.Last()), target);
         }
         }
     }
     }
-}
+}

+ 1 - 1
PixiEditor/Models/DataHolders/Document/Document.Constructors.cs

@@ -41,7 +41,7 @@ namespace PixiEditor.Models.DataHolders
             RaisePropertyChanged(nameof(LayerStructure));
             RaisePropertyChanged(nameof(LayerStructure));
             foreach (var layerGuid in e.AffectedLayerGuids)
             foreach (var layerGuid in e.AffectedLayerGuids)
             {
             {
-                Layer layer = Layers.First(x => x.LayerGuid == layerGuid);
+                Layer layer = Layers.First(x => x.GuidValue == layerGuid);
                 layer.RaisePropertyChange(nameof(layer.IsVisible));
                 layer.RaisePropertyChange(nameof(layer.IsVisible));
                 layer.RaisePropertyChange(nameof(layer.Opacity));
                 layer.RaisePropertyChange(nameof(layer.Opacity));
             }
             }

+ 41 - 41
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -9,7 +9,6 @@ using SkiaSharp;
 using System;
 using System;
 using System.Buffers;
 using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Windows;
 using System.Windows;
@@ -78,7 +77,7 @@ namespace PixiEditor.Models.DataHolders
             get => referenceLayerRenderer;
             get => referenceLayerRenderer;
         }
         }
 
 
-        public Layer ActiveLayer => Layers.Count > 0 ? Layers.FirstOrDefault(x => x.LayerGuid == ActiveLayerGuid) : null;
+        public Layer ActiveLayer => Layers.Count > 0 ? Layers.FirstOrDefault(x => x.GuidValue == ActiveLayerGuid) : null;
 
 
         public Guid ActiveLayerGuid
         public Guid ActiveLayerGuid
         {
         {
@@ -111,7 +110,7 @@ namespace PixiEditor.Models.DataHolders
                 }
                 }
             }
             }
 
 
-            ActiveLayerGuid = Layers[index].LayerGuid;
+            ActiveLayerGuid = Layers[index].GuidValue;
             ActiveLayer.IsActive = true;
             ActiveLayer.IsActive = true;
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(ActiveLayerGuid, LayerAction.SetActive));
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(ActiveLayerGuid, LayerAction.SetActive));
         }
         }
@@ -126,7 +125,7 @@ namespace PixiEditor.Models.DataHolders
         {
         {
             foreach (var layer in Layers)
             foreach (var layer in Layers)
             {
             {
-                if (layer.LayerGuid == ActiveLayerGuid)
+                if (layer.GuidValue == ActiveLayerGuid)
                 {
                 {
                     layer.LayerHighlightColor = MainSelectedLayerColor;
                     layer.LayerHighlightColor = MainSelectedLayerColor;
                 }
                 }
@@ -141,7 +140,7 @@ namespace PixiEditor.Models.DataHolders
         {
         {
             var args = new object[] { layerGuid, referenceLayer, above };
             var args = new object[] { layerGuid, referenceLayer, above };
 
 
-            Layer layer = Layers.First(x => x.LayerGuid == layerGuid);
+            Layer layer = Layers.First(x => x.GuidValue == layerGuid);
 
 
             int oldIndex = Layers.IndexOf(layer);
             int oldIndex = Layers.IndexOf(layer);
 
 
@@ -167,8 +166,8 @@ namespace PixiEditor.Models.DataHolders
         {
         {
             var args = new object[] { groupGuid, referenceLayer, above };
             var args = new object[] { groupGuid, referenceLayer, above };
 
 
-            var topLayer = Layers.First(x => x.LayerGuid == LayerStructure.GetGroupByGuid(groupGuid).EndLayerGuid);
-            var bottomLayer = Layers.First(x => x.LayerGuid == LayerStructure.GetGroupByGuid(groupGuid).StartLayerGuid);
+            var topLayer = Layers.First(x => x.GuidValue == LayerStructure.GetGroupByGuid(groupGuid).EndLayerGuid);
+            var bottomLayer = Layers.First(x => x.GuidValue == LayerStructure.GetGroupByGuid(groupGuid).StartLayerGuid);
 
 
             int indexOfTopLayer = Layers.IndexOf(topLayer);
             int indexOfTopLayer = Layers.IndexOf(topLayer);
             Guid oldReferenceLayerGuid;
             Guid oldReferenceLayerGuid;
@@ -176,12 +175,12 @@ namespace PixiEditor.Models.DataHolders
 
 
             if (indexOfTopLayer + 1 < Layers.Count)
             if (indexOfTopLayer + 1 < Layers.Count)
             {
             {
-                oldReferenceLayerGuid = topLayer.LayerGuid;
+                oldReferenceLayerGuid = topLayer.GuidValue;
             }
             }
             else
             else
             {
             {
                 int indexOfBottomLayer = Layers.IndexOf(bottomLayer);
                 int indexOfBottomLayer = Layers.IndexOf(bottomLayer);
-                oldReferenceLayerGuid = Layers[indexOfBottomLayer - 1].LayerGuid;
+                oldReferenceLayerGuid = Layers[indexOfBottomLayer - 1].GuidValue;
                 oldAbove = true;
                 oldAbove = true;
             }
             }
 
 
@@ -241,12 +240,12 @@ namespace PixiEditor.Models.DataHolders
                 UndoManager.AddUndoChange(
                 UndoManager.AddUndoChange(
                     storageChange.ToChange(
                     storageChange.ToChange(
                         RemoveLayerProcess,
                         RemoveLayerProcess,
-                        new object[] { Layers[^1].LayerGuid },
+                        new object[] { Layers[^1].GuidValue },
                         RestoreLayersProcess,
                         RestoreLayersProcess,
                         "Add layer"));
                         "Add layer"));
             }
             }
 
 
-            LayersChanged?.Invoke(this, new LayersChangedEventArgs(Layers[^1].LayerGuid, LayerAction.Add));
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(Layers[^1].GuidValue, LayerAction.Add));
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -267,7 +266,7 @@ namespace PixiEditor.Models.DataHolders
             UndoManager.AddUndoChange(
             UndoManager.AddUndoChange(
                 storageChange.ToChange(
                 storageChange.ToChange(
                     RemoveLayerProcess,
                     RemoveLayerProcess,
-                    new object[] { duplicate.LayerGuid },
+                    new object[] { duplicate.GuidValue },
                     RestoreLayersProcess,
                     RestoreLayersProcess,
                     "Duplicate Layer"));
                     "Duplicate Layer"));
 
 
@@ -294,9 +293,9 @@ namespace PixiEditor.Models.DataHolders
             var selectedLayers = Layers.Where(x => x.IsActive);
             var selectedLayers = Layers.Where(x => x.IsActive);
             foreach (var layer in selectedLayers)
             foreach (var layer in selectedLayers)
             {
             {
-                if (layer.LayerGuid != lastLayerGuid)
+                if (layer.GuidValue != lastLayerGuid)
                 {
                 {
-                    ActiveLayerGuid = layer.LayerGuid;
+                    ActiveLayerGuid = layer.GuidValue;
                     LayersChanged?.Invoke(this, new LayersChangedEventArgs(ActiveLayerGuid, LayerAction.SetActive));
                     LayersChanged?.Invoke(this, new LayersChangedEventArgs(ActiveLayerGuid, LayerAction.SetActive));
                     return;
                     return;
                 }
                 }
@@ -313,9 +312,9 @@ namespace PixiEditor.Models.DataHolders
                     return;
                     return;
                 }
                 }
 
 
-                if (ActiveLayerGuid == layer.LayerGuid)
+                if (ActiveLayerGuid == layer.GuidValue)
                 {
                 {
-                    SetNextSelectedLayerAsActive(layer.LayerGuid);
+                    SetNextSelectedLayerAsActive(layer.GuidValue);
                 }
                 }
 
 
                 layer.IsActive = !layer.IsActive;
                 layer.IsActive = !layer.IsActive;
@@ -358,15 +357,15 @@ namespace PixiEditor.Models.DataHolders
                 return;
                 return;
             }
             }
 
 
-            LayerStructure.AssignParent(Layers[layerIndex].LayerGuid, null);
+            LayerStructure.AssignParent(Layers[layerIndex].GuidValue, null);
 
 
             bool wasActive = Layers[layerIndex].IsActive;
             bool wasActive = Layers[layerIndex].IsActive;
 
 
-            StorageBasedChange change = new(this, new[] { Layers[layerIndex] });
             if (addToUndo)
             if (addToUndo)
             {
             {
+                StorageBasedChange change = new(this, new[] { Layers[layerIndex] });
                 UndoManager.AddUndoChange(
                 UndoManager.AddUndoChange(
-                    change.ToChange(RestoreLayersProcess, RemoveLayerProcess, new object[] { Layers[layerIndex].LayerGuid }));
+                    change.ToChange(RestoreLayersProcess, RemoveLayerProcess, new object[] { Layers[layerIndex].GuidValue }));
             }
             }
 
 
             Layers.RemoveAt(layerIndex);
             Layers.RemoveAt(layerIndex);
@@ -393,7 +392,7 @@ namespace PixiEditor.Models.DataHolders
             Layer[] layers = Layers.Where(x => x.IsActive).ToArray();
             Layer[] layers = Layers.Where(x => x.IsActive).ToArray();
             int firstIndex = Layers.IndexOf(layers[0]);
             int firstIndex = Layers.IndexOf(layers[0]);
 
 
-            object[] guidArgs = new object[] { layers.Select(x => x.LayerGuid).ToArray() };
+            object[] guidArgs = new object[] { layers.Select(x => x.GuidValue).ToArray() };
 
 
             StorageBasedChange change = new(this, layers);
             StorageBasedChange change = new(this, layers);
 
 
@@ -440,11 +439,11 @@ namespace PixiEditor.Models.DataHolders
 
 
             Layer mergedLayer = layersToMerge[0];
             Layer mergedLayer = layersToMerge[0];
 
 
-            var groupParent = LayerStructure.GetGroupByLayer(layersToMerge[^1].LayerGuid);
+            var groupParent = LayerStructure.GetGroupByLayer(layersToMerge[^1].GuidValue);
 
 
             Layer placeholderLayer = new("_placeholder");
             Layer placeholderLayer = new("_placeholder");
             Layers.Insert(index, placeholderLayer);
             Layers.Insert(index, placeholderLayer);
-            LayerStructure.AssignParent(placeholderLayer.LayerGuid, groupParent?.GroupGuid);
+            LayerStructure.AssignParent(placeholderLayer.GuidValue, groupParent?.GroupGuid);
 
 
             for (int i = 0; i < layersToMerge.Length - 1; i++)
             for (int i = 0; i < layersToMerge.Length - 1; i++)
             {
             {
@@ -455,10 +454,9 @@ namespace PixiEditor.Models.DataHolders
             }
             }
 
 
             Layers.Insert(index, mergedLayer);
             Layers.Insert(index, mergedLayer);
-            LayerStructure.AssignParent(mergedLayer.LayerGuid, groupParent?.GroupGuid);
+            LayerStructure.AssignParent(mergedLayer.GuidValue, groupParent?.GroupGuid);
 
 
             RemoveLayer(placeholderLayer, false);
             RemoveLayer(placeholderLayer, false);
-
             RemoveLayer(layersToMerge[^1], false);
             RemoveLayer(layersToMerge[^1], false);
 
 
             SetMainActiveLayer(Layers.IndexOf(mergedLayer));
             SetMainActiveLayer(Layers.IndexOf(mergedLayer));
@@ -489,9 +487,9 @@ namespace PixiEditor.Models.DataHolders
                 InsertLayersAtIndexesProcess,
                 InsertLayersAtIndexesProcess,
                 new object[] { indexes[0] },
                 new object[] { indexes[0] },
                 MergeLayersProcess,
                 MergeLayersProcess,
-                new object[] { indexes, nameIsLastLayers, layer.LayerGuid }));
+                new object[] { indexes, nameIsLastLayers, layer.GuidValue }));
 
 
-            UndoManager.SquashUndoChanges(2, "Undo merge layers");
+            UndoManager.SquashUndoChanges(2, "Undo merge layers", false);
 
 
             return layer;
             return layer;
         }
         }
@@ -532,13 +530,13 @@ namespace PixiEditor.Models.DataHolders
             int indexTo = (int)props[0];
             int indexTo = (int)props[0];
             Guid layerGuid = (Guid)props[1];
             Guid layerGuid = (Guid)props[1];
 
 
-            Guid layerAtOldIndex = Layers[indexTo].LayerGuid;
+            Guid layerAtOldIndex = Layers[indexTo].GuidValue;
 
 
             var startGroup = LayerStructure.GetGroupByLayer(layerGuid);
             var startGroup = LayerStructure.GetGroupByLayer(layerGuid);
 
 
             LayerStructure.PreMoveReassignBounds(new GroupData(startGroup?.GroupGuid), layerGuid);
             LayerStructure.PreMoveReassignBounds(new GroupData(startGroup?.GroupGuid), layerGuid);
 
 
-            Layers.Move(Layers.IndexOf(Layers.First(x => x.LayerGuid == layerGuid)), indexTo);
+            Layers.Move(Layers.IndexOf(Layers.First(x => x.GuidValue == layerGuid)), indexTo);
 
 
             var newGroup = LayerStructure.GetGroupByLayer(layerAtOldIndex);
             var newGroup = LayerStructure.GetGroupByLayer(layerAtOldIndex);
 
 
@@ -599,7 +597,8 @@ namespace PixiEditor.Models.DataHolders
         {
         {
             if (args.Length > 0 && args[0] is int layerIndex)
             if (args.Length > 0 && args[0] is int layerIndex)
             {
             {
-                Layers.RemoveAt(layerIndex);
+                RemoveLayer(layerIndex, false);
+
                 for (int i = 0; i < layers.Length; i++)
                 for (int i = 0; i < layers.Length; i++)
                 {
                 {
                     Layer layer = layers[i];
                     Layer layer = layers[i];
@@ -607,7 +606,7 @@ namespace PixiEditor.Models.DataHolders
                     Layers.Insert(data[i].LayerIndex, layer);
                     Layers.Insert(data[i].LayerIndex, layer);
                 }
                 }
 
 
-                ActiveLayerGuid = layers.First(x => x.LayerHighlightColor == MainSelectedLayerColor).LayerGuid;
+                ActiveLayerGuid = layers.First(x => x.LayerHighlightColor == MainSelectedLayerColor).GuidValue;
                 // Identifying main layer by highlightColor is a bit hacky, but shhh
                 // Identifying main layer by highlightColor is a bit hacky, but shhh
             }
             }
         }
         }
@@ -645,15 +644,15 @@ namespace PixiEditor.Models.DataHolders
             GuidStructureItem group = LayerStructure.GetGroupByGuid(groupGuid);
             GuidStructureItem group = LayerStructure.GetGroupByGuid(groupGuid);
             GuidStructureItem referenceLayerGroup = LayerStructure.GetGroupByLayer(referenceLayerGuid);
             GuidStructureItem referenceLayerGroup = LayerStructure.GetGroupByLayer(referenceLayerGuid);
 
 
-            Layer referenceLayer = Layers.First(x => x.LayerGuid == referenceLayerGuid);
+            Layer referenceLayer = Layers.First(x => x.GuidValue == referenceLayerGuid);
 
 
             int layerIndex = Layers.IndexOf(referenceLayer);
             int layerIndex = Layers.IndexOf(referenceLayer);
-            int folderTopIndex = Layers.IndexOf(Layers.First(x => x.LayerGuid == group?.EndLayerGuid));
+            int folderTopIndex = Layers.IndexOf(Layers.First(x => x.GuidValue == group?.EndLayerGuid));
             int oldIndex = folderTopIndex;
             int oldIndex = folderTopIndex;
 
 
             if (layerIndex < folderTopIndex)
             if (layerIndex < folderTopIndex)
             {
             {
-                int folderBottomIndex = Layers.IndexOf(Layers.First(x => x.LayerGuid == group.StartLayerGuid));
+                int folderBottomIndex = Layers.IndexOf(Layers.First(x => x.GuidValue == group.StartLayerGuid));
                 oldIndex = folderBottomIndex;
                 oldIndex = folderBottomIndex;
             }
             }
 
 
@@ -690,8 +689,8 @@ namespace PixiEditor.Models.DataHolders
             Guid referenceLayer = (Guid)parameter[1];
             Guid referenceLayer = (Guid)parameter[1];
             bool above = (bool)parameter[2];
             bool above = (bool)parameter[2];
 
 
-            int layerIndex = Layers.IndexOf(Layers.First(x => x.LayerGuid == referenceLayer));
-            int oldIndex = Layers.IndexOf(Layers.First(x => x.LayerGuid == layer));
+            int layerIndex = Layers.IndexOf(Layers.First(x => x.GuidValue == referenceLayer));
+            int oldIndex = Layers.IndexOf(Layers.First(x => x.GuidValue == layer));
             int newIndex = CalculateNewIndex(layerIndex, above, oldIndex);
             int newIndex = CalculateNewIndex(layerIndex, above, oldIndex);
 
 
             var startGroup = LayerStructure.GetGroupByLayer(layer);
             var startGroup = LayerStructure.GetGroupByLayer(layer);
@@ -710,6 +709,7 @@ namespace PixiEditor.Models.DataHolders
             }
             }
 
 
             RaisePropertyChanged(nameof(LayerStructure));
             RaisePropertyChanged(nameof(LayerStructure));
+            Renderer.ForceRerender();
         }
         }
 
 
         private void RestoreLayersProcess(Layer[] layers, UndoLayer[] layersData)
         private void RestoreLayersProcess(Layer[] layers, UndoLayer[] layersData)
@@ -728,21 +728,21 @@ namespace PixiEditor.Models.DataHolders
 
 
         private void RemoveLayerProcess(object[] parameters)
         private void RemoveLayerProcess(object[] parameters)
         {
         {
-            if (parameters != null && parameters.Length > 0 && parameters[0] is Guid layerGuid)
+            if (parameters is { Length: > 0 } && parameters[0] is Guid layerGuid)
             {
             {
-                Layer layer = Layers.First(x => x.LayerGuid == layerGuid);
+                Layer layer = Layers.First(x => x.GuidValue == layerGuid);
                 int index = Layers.IndexOf(layer);
                 int index = Layers.IndexOf(layer);
                 bool wasActive = layer.IsActive;
                 bool wasActive = layer.IsActive;
 
 
-                var layerGroup = LayerStructure.GetGroupByLayer(layer.LayerGuid);
+                var layerGroup = LayerStructure.GetGroupByLayer(layer.GuidValue);
 
 
                 LayerStructure.ExpandParentGroups(layerGroup);
                 LayerStructure.ExpandParentGroups(layerGroup);
 
 
-                if (layerGroup?.Parent != null && LayerStructure.GroupContainsOnlyLayer(layer.LayerGuid, layerGroup))
+                if (layerGroup?.Parent != null && LayerStructure.GroupContainsOnlyLayer(layer.GuidValue, layerGroup))
                 {
                 {
                     LayerStructure.PreMoveReassignBounds(new GroupData(layerGroup.Parent.GroupGuid), new GroupData(layerGroup.GroupGuid));
                     LayerStructure.PreMoveReassignBounds(new GroupData(layerGroup.Parent.GroupGuid), new GroupData(layerGroup.GroupGuid));
                 }
                 }
-                LayerStructure.AssignParent(Layers[index].LayerGuid, null);
+                LayerStructure.AssignParent(Layers[index].GuidValue, null);
                 RemoveGroupsIfEmpty(layer, layerGroup);
                 RemoveGroupsIfEmpty(layer, layerGroup);
 
 
                 Layers.Remove(layer);
                 Layers.Remove(layer);
@@ -758,7 +758,7 @@ namespace PixiEditor.Models.DataHolders
 
 
         private void RemoveGroupsIfEmpty(Layer layer, GuidStructureItem layerGroup)
         private void RemoveGroupsIfEmpty(Layer layer, GuidStructureItem layerGroup)
         {
         {
-            if (LayerStructure.GroupContainsOnlyLayer(layer.LayerGuid, layerGroup))
+            if (LayerStructure.GroupContainsOnlyLayer(layer.GuidValue, layerGroup))
             {
             {
                 if (layerGroup.Parent != null)
                 if (layerGroup.Parent != null)
                 {
                 {

+ 4 - 1
PixiEditor/Models/DataHolders/Surface.cs

@@ -15,7 +15,10 @@ namespace PixiEditor.Models.DataHolders
 {
 {
     public class Surface : IDisposable
     public class Surface : IDisposable
     {
     {
-        public static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
+        public static SKPaint ReplacingPaint { get; } = new() { BlendMode = SKBlendMode.Src };
+        public static SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
+        public static SKPaint MaskingPaint { get; } = new() { BlendMode = SKBlendMode.DstIn };
+        public static SKPaint InverseMaskingPaint { get; } = new() { BlendMode = SKBlendMode.DstOut };
 
 
         private static readonly SKPaint nearestNeighborReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src, FilterQuality = SKFilterQuality.None };
         private static readonly SKPaint nearestNeighborReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src, FilterQuality = SKFilterQuality.None };
 
 

+ 12 - 7
PixiEditor/Models/IO/Exporter.cs

@@ -73,21 +73,26 @@ namespace PixiEditor.Models.IO
 
 
         public static void SaveAsGZippedBytes(string path, Surface surface)
         public static void SaveAsGZippedBytes(string path, Surface surface)
         {
         {
-            var imageInfo = new SKImageInfo(surface.Width, surface.Height, SKColorType.RgbaF16);
-            var unmanagedBuffer = Marshal.AllocHGlobal(surface.Width * surface.Height * 8);
+            SaveAsGZippedBytes(path, surface, SKRectI.Create(0, 0, surface.Width, surface.Height));
+        }
+
+        public static void SaveAsGZippedBytes(string path, Surface surface, SKRectI rectToSave)
+        {
+            var imageInfo = new SKImageInfo(rectToSave.Width, rectToSave.Height, SKColorType.RgbaF16);
+            var unmanagedBuffer = Marshal.AllocHGlobal(rectToSave.Width * rectToSave.Height * 8);
             //+8 bytes for width and height
             //+8 bytes for width and height
-            var bytes = new byte[surface.Width * surface.Height * 8 + 8];
+            var bytes = new byte[rectToSave.Width * rectToSave.Height * 8 + 8];
             try
             try
             {
             {
-                surface.SkiaSurface.ReadPixels(imageInfo, unmanagedBuffer, surface.Width * 8, 0, 0);
-                Marshal.Copy(unmanagedBuffer, bytes, 8, surface.Width * surface.Height * 8);
+                surface.SkiaSurface.ReadPixels(imageInfo, unmanagedBuffer, rectToSave.Width * 8, rectToSave.Left, rectToSave.Top);
+                Marshal.Copy(unmanagedBuffer, bytes, 8, rectToSave.Width * rectToSave.Height * 8);
             }
             }
             finally
             finally
             {
             {
                 Marshal.FreeHGlobal(unmanagedBuffer);
                 Marshal.FreeHGlobal(unmanagedBuffer);
             }
             }
-            BitConverter.GetBytes((int)surface.Width).CopyTo(bytes, 0);
-            BitConverter.GetBytes((int)surface.Height).CopyTo(bytes, 4);
+            BitConverter.GetBytes(rectToSave.Width).CopyTo(bytes, 0);
+            BitConverter.GetBytes(rectToSave.Height).CopyTo(bytes, 4);
             using FileStream outputStream = new(path, FileMode.Create);
             using FileStream outputStream = new(path, FileMode.Create);
             using GZipStream compressedStream = new GZipStream(outputStream, CompressionLevel.Fastest);
             using GZipStream compressedStream = new GZipStream(outputStream, CompressionLevel.Fastest);
             compressedStream.Write(bytes);
             compressedStream.Write(bytes);

+ 33 - 3
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -9,14 +9,13 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
-using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
 
 
 namespace PixiEditor.Models.ImageManipulation
 namespace PixiEditor.Models.ImageManipulation
 {
 {
     public static class BitmapUtils
     public static class BitmapUtils
     {
     {
-        public static Surface CombineLayers(Int32Rect portion, IEnumerable<Layer> layers,  LayerStructure structure = null)
+        public static Surface CombineLayers(Int32Rect portion, IEnumerable<Layer> layers, LayerStructure structure = null)
         {
         {
             Surface finalSurface = new(portion.Width, portion.Height);
             Surface finalSurface = new(portion.Width, portion.Height);
             using SKPaint paint = new();
             using SKPaint paint = new();
@@ -49,6 +48,37 @@ namespace PixiEditor.Models.ImageManipulation
             return finalSurface;
             return finalSurface;
         }
         }
 
 
+        public static Surface[] ExtractSelectedPortions(Layer selLayer, Layer[] extractFrom, bool eraceFromLayers)
+        {
+            using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
+            Surface[] output = new Surface[extractFrom.Length];
+
+            int count = 0;
+            foreach (Layer layer in extractFrom)
+            {
+                Surface portion = new Surface(selLayer.Width, selLayer.Height);
+                SKRect selLayerRect = new SKRect(0, 0, selLayer.Width, selLayer.Height);
+
+                int x = selLayer.OffsetX - layer.OffsetX;
+                int y = selLayer.OffsetY - layer.OffsetY;
+
+                using (var layerSnap = layer.LayerBitmap.SkiaSurface.Snapshot())
+                    portion.SkiaSurface.Canvas.DrawImage(layerSnap, new SKRect(x, y, x + selLayer.Width, y + selLayer.Height), selLayerRect, Surface.ReplacingPaint);
+                portion.SkiaSurface.Canvas.DrawImage(selSnap, 0, 0, Surface.MaskingPaint);
+                output[count] = portion;
+                count++;
+
+                if (eraceFromLayers)
+                {
+                    layer.LayerBitmap.SkiaSurface.Canvas.DrawImage(selSnap, new SKRect(0, 0, selLayer.Width, selLayer.Height),
+                        new SKRect(selLayer.OffsetX - layer.OffsetX, selLayer.OffsetY - layer.OffsetY, selLayer.OffsetX - layer.OffsetX + selLayer.Width, selLayer.OffsetY - layer.OffsetY + selLayer.Height),
+                        Surface.InverseMaskingPaint);
+                    layer.InvokeLayerBitmapChange(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height));
+                }
+            }
+            return output;
+        }
+
         /// <summary>
         /// <summary>
         /// Generates simplified preview from Document, very fast, great for creating small previews. Creates uniform streched image.
         /// Generates simplified preview from Document, very fast, great for creating small previews. Creates uniform streched image.
         /// </summary>
         /// </summary>
@@ -119,7 +149,7 @@ namespace PixiEditor.Models.ImageManipulation
                     var cl = layer.GetPixel(position.X, position.Y);
                     var cl = layer.GetPixel(position.X, position.Y);
                     pixels[j] = cl;
                     pixels[j] = cl;
                 }
                 }
-                result[layer.LayerGuid] = pixels;
+                result[layer.GuidValue] = pixels;
             }
             }
 
 
             return result;
             return result;

+ 5 - 5
PixiEditor/Models/Layers/BasicLayer.cs

@@ -1,10 +1,10 @@
-using System;
-using PixiEditor.Helpers;
+using PixiEditor.Helpers;
+using System;
 
 
 namespace PixiEditor.Models.Layers
 namespace PixiEditor.Models.Layers
 {
 {
     [Serializable]
     [Serializable]
-    public class BasicLayer : NotifyableObject
+    public class BasicLayer : NotifyableObject, IHasGuid
     {
     {
         private int height;
         private int height;
 
 
@@ -30,6 +30,6 @@ namespace PixiEditor.Models.Layers
             }
             }
         }
         }
 
 
-        public Guid LayerGuid { get; protected set; }
+        public Guid GuidValue { get; protected set; }
     }
     }
-}
+}

+ 9 - 0
PixiEditor/Models/Layers/IHasGuid.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace PixiEditor.Models.Layers
+{
+    public interface IHasGuid
+    {
+        Guid GuidValue { get; }
+    }
+}

+ 8 - 8
PixiEditor/Models/Layers/Layer.cs

@@ -38,7 +38,7 @@ namespace PixiEditor.Models.Layers
             IsReset = true;
             IsReset = true;
             Width = 1;
             Width = 1;
             Height = 1;
             Height = 1;
-            LayerGuid = Guid.NewGuid();
+            GuidValue = Guid.NewGuid();
         }
         }
 
 
         public Layer(string name, int width, int height)
         public Layer(string name, int width, int height)
@@ -48,7 +48,7 @@ namespace PixiEditor.Models.Layers
             IsReset = true;
             IsReset = true;
             Width = width;
             Width = width;
             Height = height;
             Height = height;
-            LayerGuid = Guid.NewGuid();
+            GuidValue = Guid.NewGuid();
         }
         }
 
 
         public Layer(string name, Surface layerBitmap)
         public Layer(string name, Surface layerBitmap)
@@ -57,7 +57,7 @@ namespace PixiEditor.Models.Layers
             LayerBitmap = layerBitmap;
             LayerBitmap = layerBitmap;
             Width = layerBitmap.Width;
             Width = layerBitmap.Width;
             Height = layerBitmap.Height;
             Height = layerBitmap.Height;
-            LayerGuid = Guid.NewGuid();
+            GuidValue = Guid.NewGuid();
         }
         }
 
 
         public Dictionary<Coordinates, SKColor> LastRelativeCoordinates { get; set; }
         public Dictionary<Coordinates, SKColor> LastRelativeCoordinates { get; set; }
@@ -119,7 +119,7 @@ namespace PixiEditor.Models.Layers
                             isVisible,
                             isVisible,
                             value,
                             value,
                             LayerHelper.FindLayerByGuidProcess,
                             LayerHelper.FindLayerByGuidProcess,
-                            new object[] { LayerGuid },
+                            new object[] { GuidValue },
                             "Change layer visibility"));
                             "Change layer visibility"));
                     IsVisible = value;
                     IsVisible = value;
                     InvokeLayerBitmapChange();
                     InvokeLayerBitmapChange();
@@ -140,7 +140,7 @@ namespace PixiEditor.Models.Layers
         public Surface LayerBitmap
         public Surface LayerBitmap
         {
         {
             get => layerBitmap;
             get => layerBitmap;
-            private set
+            set
             {
             {
                 Int32Rect prevRect = new Int32Rect(OffsetX, OffsetY, Width, Height);
                 Int32Rect prevRect = new Int32Rect(OffsetX, OffsetY, Width, Height);
                 layerBitmap = value;
                 layerBitmap = value;
@@ -178,7 +178,7 @@ namespace PixiEditor.Models.Layers
                                    opacity,
                                    opacity,
                                    value,
                                    value,
                                    LayerHelper.FindLayerByGuidProcess,
                                    LayerHelper.FindLayerByGuidProcess,
-                                   new object[] { LayerGuid },
+                                   new object[] { GuidValue },
                                    "Change layer opacity"));
                                    "Change layer opacity"));
                     Opacity = value;
                     Opacity = value;
                 }
                 }
@@ -230,7 +230,7 @@ namespace PixiEditor.Models.Layers
         /// <remarks>This is potentially destructive operation, use when absolutelly necessary.</remarks>
         /// <remarks>This is potentially destructive operation, use when absolutelly necessary.</remarks>
         public void ChangeGuid(Guid newGuid)
         public void ChangeGuid(Guid newGuid)
         {
         {
-            LayerGuid = newGuid;
+            GuidValue = newGuid;
         }
         }
 
 
         public IEnumerable<Layer> GetLayers()
         public IEnumerable<Layer> GetLayers()
@@ -252,7 +252,7 @@ namespace PixiEditor.Models.Layers
                 Opacity = Opacity,
                 Opacity = Opacity,
                 IsActive = IsActive,
                 IsActive = IsActive,
                 IsRenaming = IsRenaming,
                 IsRenaming = IsRenaming,
-                LayerGuid = generateNewGuid ? Guid.NewGuid() : LayerGuid
+                GuidValue = generateNewGuid ? Guid.NewGuid() : GuidValue
             };
             };
         }
         }
 
 

+ 21 - 16
PixiEditor/Models/Layers/LayerGroup.cs

@@ -1,28 +1,33 @@
-using System;
-using System.Collections;
+using PixiEditor.Helpers;
+using PixiEditor.ViewModels;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
-using PixiEditor.Helpers;
-using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.Layers
 namespace PixiEditor.Models.Layers
 {
 {
-    public class LayerGroup : NotifyableObject
+    public class LayerGroup : NotifyableObject, IHasGuid
     {
     {
-        public Guid GroupGuid { get; init; }
+        public Guid GuidValue { get; init; }
 
 
-        public GuidStructureItem StructureData { get; init; }
+        private GuidStructureItem structureData;
+        public GuidStructureItem StructureData
+        {
+            get => structureData;
+            set => SetProperty(ref structureData, value);
+        }
 
 
-        public ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
+        private ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
 
 
-        public ObservableCollection<LayerGroup> Subfolders { get; set; } = new ObservableCollection<LayerGroup>();
+        private ObservableCollection<LayerGroup> Subfolders { get; set; } = new ObservableCollection<LayerGroup>();
 
 
-        public IEnumerable Items => BuildItems();
+        private ObservableCollection<IHasGuid> items = null;
+        public ObservableCollection<IHasGuid> Items => items ??= BuildItems();
 
 
-        private IEnumerable BuildItems()
+        private ObservableCollection<IHasGuid> BuildItems()
         {
         {
-            List<object> obj = new(Layers.Reverse());
+            List<IHasGuid> obj = new(Layers.Reverse());
             foreach (var subfolder in Subfolders)
             foreach (var subfolder in Subfolders)
             {
             {
                 obj.Insert(Math.Clamp(subfolder.DisplayIndex - DisplayIndex, 0, obj.Count), subfolder);
                 obj.Insert(Math.Clamp(subfolder.DisplayIndex - DisplayIndex, 0, obj.Count), subfolder);
@@ -30,7 +35,7 @@ namespace PixiEditor.Models.Layers
 
 
             obj.Reverse();
             obj.Reverse();
 
 
-            return obj;
+            return new ObservableCollection<IHasGuid>(obj);
         }
         }
 
 
         private string name;
         private string name;
@@ -95,7 +100,7 @@ namespace PixiEditor.Models.Layers
 
 
         private void UpdateIsExpandedInDocument(bool value)
         private void UpdateIsExpandedInDocument(bool value)
         {
         {
-            var folder = ViewModelMain.Current.BitmapManager.ActiveDocument.LayerStructure.GetGroupByGuid(GroupGuid);
+            var folder = ViewModelMain.Current.BitmapManager.ActiveDocument.LayerStructure.GetGroupByGuid(GuidValue);
             if (folder != null)
             if (folder != null)
             {
             {
                 folder.IsExpanded = value;
                 folder.IsExpanded = value;
@@ -114,10 +119,10 @@ namespace PixiEditor.Models.Layers
             Layers = new ObservableCollection<Layer>(layers);
             Layers = new ObservableCollection<Layer>(layers);
             Subfolders = new ObservableCollection<LayerGroup>(subfolders);
             Subfolders = new ObservableCollection<LayerGroup>(subfolders);
             Name = name;
             Name = name;
-            GroupGuid = guid;
+            GuidValue = guid;
             DisplayIndex = displayIndex;
             DisplayIndex = displayIndex;
             TopIndex = topIndex;
             TopIndex = topIndex;
             StructureData = structureData;
             StructureData = structureData;
         }
         }
     }
     }
-}
+}

+ 8 - 15
PixiEditor/Models/Layers/LayerHelper.cs

@@ -1,4 +1,5 @@
-using PixiEditor.Models.DataHolders;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using System;
 using System;
@@ -11,7 +12,7 @@ namespace PixiEditor.Models.Layers
     {
     {
         public static Layer FindLayerByGuid(Document document, Guid guid)
         public static Layer FindLayerByGuid(Document document, Guid guid)
         {
         {
-            return document.Layers.FirstOrDefault(x => x.LayerGuid == guid);
+            return document.Layers.FirstOrDefault(x => x.GuidValue == guid);
         }
         }
 
 
         public static object FindLayerByGuidProcess(object[] parameters)
         public static object FindLayerByGuidProcess(object[] parameters)
@@ -58,24 +59,16 @@ namespace PixiEditor.Models.Layers
 
 
         public static Layer MergeWith(this Layer thisLayer, Layer otherLayer, string newName, Vector documentsSize)
         public static Layer MergeWith(this Layer thisLayer, Layer otherLayer, string newName, Vector documentsSize)
         {
         {
-            thisLayer.GetCloser(otherLayer, out Layer xCloser, out Layer yCloser, out Layer xOther, out Layer yOther);
+            Int32Rect thisRect = new(thisLayer.OffsetX, thisLayer.OffsetY, thisLayer.Width, thisLayer.Height);
+            Int32Rect otherRect = new(otherLayer.OffsetX, otherLayer.OffsetY, otherLayer.Width, otherLayer.Height);
 
 
-            // Calculate the offset to the other layer
-            int offsetX = Math.Abs(xCloser.OffsetX + xCloser.Width - xOther.OffsetX);
-            int offsetY = Math.Abs(yCloser.OffsetY + yCloser.Height - yOther.OffsetY);
+            Int32Rect combined = thisRect.Expand(otherRect);
 
 
-            // Calculate the needed width and height of the new layer
-            int width = xCloser.Width + offsetX + xOther.Width;
-            int height = yCloser.Height + offsetY + yOther.Height;
+            Surface mergedBitmap = BitmapUtils.CombineLayers(combined, new Layer[] { thisLayer, otherLayer });
 
 
-            // Merge both layers into a bitmap
-            Surface mergedBitmap = BitmapUtils.CombineLayers(new Int32Rect(0, 0, (int)documentsSize.X, (int)documentsSize.Y), new Layer[] { thisLayer, otherLayer });
-            mergedBitmap = mergedBitmap.Crop(xCloser.OffsetX, yCloser.OffsetY, width, height);
-
-            // Create the new layer with the merged bitmap
             Layer mergedLayer = new Layer(newName, mergedBitmap)
             Layer mergedLayer = new Layer(newName, mergedBitmap)
             {
             {
-                Offset = new Thickness(xCloser.OffsetX, yCloser.OffsetY, 0, 0)
+                Offset = new Thickness(combined.X, combined.Y, 0, 0)
             };
             };
 
 
             return mergedLayer;
             return mergedLayer;

+ 18 - 18
PixiEditor/Models/Layers/LayerStructure.cs

@@ -175,8 +175,8 @@ namespace PixiEditor.Models.Layers
             var group = GetGroupByGuid(groupGuid);
             var group = GetGroupByGuid(groupGuid);
             var parentGroup = group.Parent;
             var parentGroup = group.Parent;
             bool reverseOrder = true;
             bool reverseOrder = true;
-            int groupTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.EndLayerGuid));
-            int groupBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.StartLayerGuid));
+            int groupTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == group.EndLayerGuid));
+            int groupBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == group.StartLayerGuid));
 
 
             int difference = newIndex - groupTopIndex;
             int difference = newIndex - groupTopIndex;
 
 
@@ -336,7 +336,7 @@ namespace PixiEditor.Models.Layers
             var layerGuids = GetGroupLayerGuids(group);
             var layerGuids = GetGroupLayerGuids(group);
             foreach (var layerGuid in layerGuids)
             foreach (var layerGuid in layerGuids)
             {
             {
-                layers.Add(Owner.Layers.First(x => x.LayerGuid == layerGuid));
+                layers.Add(Owner.Layers.First(x => x.GuidValue == layerGuid));
             }
             }
 
 
             return layers;
             return layers;
@@ -390,8 +390,8 @@ namespace PixiEditor.Models.Layers
         /// <returns>List of layer guids.</returns>
         /// <returns>List of layer guids.</returns>
         private List<Guid> GetGroupLayerGuids(GuidStructureItem group)
         private List<Guid> GetGroupLayerGuids(GuidStructureItem group)
         {
         {
-            Layer layerTop = Owner.Layers.FirstOrDefault(x => x.LayerGuid == group.EndLayerGuid);
-            Layer layerBottom = Owner.Layers.FirstOrDefault(x => x.LayerGuid == group.StartLayerGuid);
+            Layer layerTop = Owner.Layers.FirstOrDefault(x => x.GuidValue == group.EndLayerGuid);
+            Layer layerBottom = Owner.Layers.FirstOrDefault(x => x.GuidValue == group.StartLayerGuid);
 
 
             if (layerTop == null || layerBottom == null)
             if (layerTop == null || layerBottom == null)
             {
             {
@@ -412,7 +412,7 @@ namespace PixiEditor.Models.Layers
 
 
             for (int i = minIndex; i <= maxIndex; i++)
             for (int i = minIndex; i <= maxIndex; i++)
             {
             {
-                layerGuids.Add(Owner.Layers[i].LayerGuid);
+                layerGuids.Add(Owner.Layers[i].GuidValue);
             }
             }
 
 
             return layerGuids;
             return layerGuids;
@@ -488,10 +488,10 @@ namespace PixiEditor.Models.Layers
                 Guid? oldStart = parentGroup.StartLayerGuid;
                 Guid? oldStart = parentGroup.StartLayerGuid;
                 Guid? oldEnd = parentGroup.EndLayerGuid;
                 Guid? oldEnd = parentGroup.EndLayerGuid;
 
 
-                int layerIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == layerGuid));
+                int layerIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == layerGuid));
 
 
-                int folderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.EndLayerGuid));
-                int folderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.StartLayerGuid));
+                int folderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == parentGroup.EndLayerGuid));
+                int folderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == parentGroup.StartLayerGuid));
 
 
                 int finalTopIndex = Math.Max(folderTopIndex, layerIndex);
                 int finalTopIndex = Math.Max(folderTopIndex, layerIndex);
                 int finalBottomIndex = Math.Min(folderBottomIndex, layerIndex);
                 int finalBottomIndex = Math.Min(folderBottomIndex, layerIndex);
@@ -535,11 +535,11 @@ namespace PixiEditor.Models.Layers
             {
             {
                 Guid oldStart = parentGroup.StartLayerGuid;
                 Guid oldStart = parentGroup.StartLayerGuid;
                 Guid oldEnd = parentGroup.EndLayerGuid;
                 Guid oldEnd = parentGroup.EndLayerGuid;
-                int folderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.EndLayerGuid));
-                int folderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == group.StartLayerGuid));
+                int folderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == group.EndLayerGuid));
+                int folderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == group.StartLayerGuid));
 
 
-                int parentFolderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.EndLayerGuid));
-                int parentFolderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentGroup.StartLayerGuid));
+                int parentFolderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == parentGroup.EndLayerGuid));
+                int parentFolderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == parentGroup.StartLayerGuid));
 
 
                 int finalTopIndex = Math.Max(folderTopIndex, parentFolderTopIndex);
                 int finalTopIndex = Math.Max(folderTopIndex, parentFolderTopIndex);
                 int finalBottomIndex = Math.Min(folderBottomIndex, parentFolderBottomIndex);
                 int finalBottomIndex = Math.Min(folderBottomIndex, parentFolderBottomIndex);
@@ -639,8 +639,8 @@ namespace PixiEditor.Models.Layers
 
 
         private Guid FindBoundLayer(GuidStructureItem parentFolder, Guid layerGuid, bool above)
         private Guid FindBoundLayer(GuidStructureItem parentFolder, Guid layerGuid, bool above)
         {
         {
-            int parentFolderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentFolder.EndLayerGuid));
-            int parentFolderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.LayerGuid == parentFolder.StartLayerGuid));
+            int parentFolderTopIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == parentFolder.EndLayerGuid));
+            int parentFolderBottomIndex = Owner.Layers.IndexOf(Owner.Layers.First(x => x.GuidValue == parentFolder.StartLayerGuid));
 
 
             return FindBoundLayer(layerGuid, parentFolderTopIndex, parentFolderBottomIndex, above);
             return FindBoundLayer(layerGuid, parentFolderTopIndex, parentFolderBottomIndex, above);
         }
         }
@@ -652,7 +652,7 @@ namespace PixiEditor.Models.Layers
             for (int i = 0; i < layers.Count; i++)
             for (int i = 0; i < layers.Count; i++)
             {
             {
                 Guid layerGuid = layerGuids[i];
                 Guid layerGuid = layerGuids[i];
-                var layer = Owner.Layers.First(x => x.LayerGuid == layerGuid);
+                var layer = Owner.Layers.First(x => x.GuidValue == layerGuid);
                 int layerIndex = Owner.Layers.IndexOf(layer);
                 int layerIndex = Owner.Layers.IndexOf(layer);
                 Owner.Layers.Move(layerIndex, layerIndex + moveBy);
                 Owner.Layers.Move(layerIndex, layerIndex + moveBy);
             }
             }
@@ -662,8 +662,8 @@ namespace PixiEditor.Models.Layers
         {
         {
             foreach (var currentGroup in groups)
             foreach (var currentGroup in groups)
             {
             {
-                var endLayer = Owner.Layers.First(x => x.LayerGuid == currentGroup.EndLayerGuid);
-                var startLayer = Owner.Layers.First(x => x.LayerGuid == currentGroup.StartLayerGuid);
+                var endLayer = Owner.Layers.First(x => x.GuidValue == currentGroup.EndLayerGuid);
+                var startLayer = Owner.Layers.First(x => x.GuidValue == currentGroup.StartLayerGuid);
 
 
                 int topIndex = Owner.Layers.IndexOf(endLayer);
                 int topIndex = Owner.Layers.IndexOf(endLayer);
                 int bottomIndex = Owner.Layers.IndexOf(startLayer);
                 int bottomIndex = Owner.Layers.IndexOf(startLayer);

+ 17 - 18
PixiEditor/Models/Layers/StructuredLayerTree.cs

@@ -1,10 +1,9 @@
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
-using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.DataHolders;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
-using PixiEditor.Models.DataHolders;
 
 
 namespace PixiEditor.Models.Layers
 namespace PixiEditor.Models.Layers
 {
 {
@@ -12,7 +11,7 @@ namespace PixiEditor.Models.Layers
     {
     {
         private List<Guid> layersInStructure = new();
         private List<Guid> layersInStructure = new();
 
 
-        public WpfObservableRangeCollection<object> RootDirectoryItems { get; } = new WpfObservableRangeCollection<object>();
+        public WpfObservableRangeCollection<IHasGuid> RootDirectoryItems { get; } = new WpfObservableRangeCollection<IHasGuid>();
 
 
         private static void Swap(ref int startIndex, ref int endIndex)
         private static void Swap(ref int startIndex, ref int endIndex)
         {
         {
@@ -49,7 +48,7 @@ namespace PixiEditor.Models.Layers
 
 
             for (int i = 0; i < layers.Count; i++)
             for (int i = 0; i < layers.Count; i++)
             {
             {
-                if (currentFolder != null && layers[i].LayerGuid == currentFolder.StructureData.EndLayerGuid)
+                if (currentFolder != null && layers[i].GuidValue == currentFolder.StructureData.EndLayerGuid)
                 {
                 {
                     if (unfinishedFolders.Count > 0)
                     if (unfinishedFolders.Count > 0)
                     {
                     {
@@ -65,7 +64,7 @@ namespace PixiEditor.Models.Layers
 
 
                 AssignGroup(parsedFolders, layers, ref currentFolder, ref groupsAtIndex, unfinishedFolders, i);
                 AssignGroup(parsedFolders, layers, ref currentFolder, ref groupsAtIndex, unfinishedFolders, i);
 
 
-                if (currentFolder == null && !layersInStructure.Contains(layers[i].LayerGuid))
+                if (currentFolder == null && !layersInStructure.Contains(layers[i].GuidValue))
                 {
                 {
                     RootDirectoryItems.Add(layers[i]);
                     RootDirectoryItems.Add(layers[i]);
                 }
                 }
@@ -78,9 +77,9 @@ namespace PixiEditor.Models.Layers
 
 
         private void AssignGroup(List<LayerGroup> parsedFolders, ObservableCollection<Layer> layers, ref LayerGroup currentFolder, ref List<LayerGroup> groupsAtIndex, Stack<LayerGroup> unfinishedFolders, int i)
         private void AssignGroup(List<LayerGroup> parsedFolders, ObservableCollection<Layer> layers, ref LayerGroup currentFolder, ref List<LayerGroup> groupsAtIndex, Stack<LayerGroup> unfinishedFolders, int i)
         {
         {
-            if (parsedFolders.Any(x => x.StructureData.StartLayerGuid == layers[i].LayerGuid))
+            if (parsedFolders.Any(x => x.StructureData.StartLayerGuid == layers[i].GuidValue))
             {
             {
-                groupsAtIndex = parsedFolders.Where(x => x.StructureData.StartLayerGuid == layers[i].LayerGuid).ToList();
+                groupsAtIndex = parsedFolders.Where(x => x.StructureData.StartLayerGuid == layers[i].GuidValue).ToList();
                 for (int j = 0; j < groupsAtIndex.Count; j++)
                 for (int j = 0; j < groupsAtIndex.Count; j++)
                 {
                 {
                     LayerGroup group = groupsAtIndex[j];
                     LayerGroup group = groupsAtIndex[j];
@@ -90,10 +89,10 @@ namespace PixiEditor.Models.Layers
                         unfinishedFolders.Push(currentFolder);
                         unfinishedFolders.Push(currentFolder);
                     }
                     }
 
 
-                    groupsAtIndex[j] = parsedFolders.First(x => x.StructureData.StartLayerGuid == layers[i].LayerGuid);
+                    groupsAtIndex[j] = parsedFolders.First(x => x.StructureData.StartLayerGuid == layers[i].GuidValue);
                     groupsAtIndex[j].DisplayIndex = RootDirectoryItems.Count;
                     groupsAtIndex[j].DisplayIndex = RootDirectoryItems.Count;
                     groupsAtIndex[j].TopIndex = CalculateTopIndex(group.DisplayIndex, group.StructureData, layers);
                     groupsAtIndex[j].TopIndex = CalculateTopIndex(group.DisplayIndex, group.StructureData, layers);
-                    if (groupsAtIndex[j].StructureData.EndLayerGuid != layers[i].LayerGuid)
+                    if (groupsAtIndex[j].StructureData.EndLayerGuid != layers[i].GuidValue)
                     {
                     {
                         currentFolder = groupsAtIndex[j];
                         currentFolder = groupsAtIndex[j];
                     }
                     }
@@ -103,8 +102,8 @@ namespace PixiEditor.Models.Layers
 
 
         private int CalculateTopIndex(int displayIndex, GuidStructureItem structureData, ObservableCollection<Layer> layers)
         private int CalculateTopIndex(int displayIndex, GuidStructureItem structureData, ObservableCollection<Layer> layers)
         {
         {
-            var endLayer = layers.FirstOrDefault(x => x.LayerGuid == structureData.EndLayerGuid);
-            var bottomLayer = layers.FirstOrDefault(x => x.LayerGuid == structureData.StartLayerGuid);
+            var endLayer = layers.FirstOrDefault(x => x.GuidValue == structureData.EndLayerGuid);
+            var bottomLayer = layers.FirstOrDefault(x => x.GuidValue == structureData.StartLayerGuid);
             int originalTopIndex = 0;
             int originalTopIndex = 0;
             int originalBottomIndex = 0;
             int originalBottomIndex = 0;
             if (endLayer != null)
             if (endLayer != null)
@@ -145,18 +144,18 @@ namespace PixiEditor.Models.Layers
 
 
             foreach (var guid in layersInFolder)
             foreach (var guid in layersInFolder)
             {
             {
-                var layer = layers.FirstOrDefault(x => x.LayerGuid == guid);
+                var layer = layers.FirstOrDefault(x => x.GuidValue == guid);
                 if (layer != null)
                 if (layer != null)
                 {
                 {
-                    if (!layersInStructure.Contains(layer.LayerGuid))
+                    if (!layersInStructure.Contains(layer.GuidValue))
                     {
                     {
-                        layersInStructure.Add(layer.LayerGuid);
+                        layersInStructure.Add(layer.GuidValue);
                         structureItemLayers.Add(layer);
                         structureItemLayers.Add(layer);
                     }
                     }
                 }
                 }
             }
             }
 
 
-            int displayIndex = layersInFolder.Length > 0 ? layers.IndexOf(layers.First(x => x.LayerGuid == structureItem.StartLayerGuid)) : 0;
+            int displayIndex = layersInFolder.Length > 0 ? layers.IndexOf(layers.First(x => x.GuidValue == structureItem.StartLayerGuid)) : 0;
 
 
             structureItemLayers.Reverse();
             structureItemLayers.Reverse();
 
 
@@ -171,8 +170,8 @@ namespace PixiEditor.Models.Layers
 
 
         private Guid[] GetLayersInGroup(ObservableCollection<Layer> layers, GuidStructureItem structureItem)
         private Guid[] GetLayersInGroup(ObservableCollection<Layer> layers, GuidStructureItem structureItem)
         {
         {
-            var startLayer = layers.FirstOrDefault(x => x.LayerGuid == structureItem.StartLayerGuid);
-            var endLayer = layers.FirstOrDefault(x => x.LayerGuid == structureItem.EndLayerGuid);
+            var startLayer = layers.FirstOrDefault(x => x.GuidValue == structureItem.StartLayerGuid);
+            var endLayer = layers.FirstOrDefault(x => x.GuidValue == structureItem.EndLayerGuid);
 
 
             if (startLayer == null || endLayer == null)
             if (startLayer == null || endLayer == null)
             {
             {
@@ -193,7 +192,7 @@ namespace PixiEditor.Models.Layers
 
 
             for (int i = 0; i < len; i++)
             for (int i = 0; i < len; i++)
             {
             {
-                guids[i] = layers[i + startIndex].LayerGuid;
+                guids[i] = layers[i + startIndex].GuidValue;
             }
             }
 
 
             return guids;
             return guids;

+ 2 - 2
PixiEditor/Models/Layers/Utils/LayerStructureUtils.cs

@@ -16,7 +16,7 @@ namespace PixiEditor.Models.Layers.Utils
                 return 0f;
                 return 0f;
             }
             }
 
 
-            var group = structure.GetGroupByLayer(layer.LayerGuid);
+            var group = structure.GetGroupByLayer(layer.GuidValue);
             GuidStructureItem groupToCheck = group;
             GuidStructureItem groupToCheck = group;
             float finalOpacity = layer.Opacity;
             float finalOpacity = layer.Opacity;
 
 
@@ -41,7 +41,7 @@ namespace PixiEditor.Models.Layers.Utils
                 return false;
                 return false;
             }
             }
 
 
-            var group = structure.GetGroupByLayer(layer.LayerGuid);
+            var group = structure.GetGroupByLayer(layer.GuidValue);
             bool atLeastOneParentIsInvisible = false;
             bool atLeastOneParentIsInvisible = false;
             GuidStructureItem groupToCheck = group;
             GuidStructureItem groupToCheck = group;
             while (groupToCheck != null)
             while (groupToCheck != null)

+ 38 - 5
PixiEditor/Models/Tools/BitmapOperationTool.cs

@@ -1,9 +1,11 @@
-using PixiEditor.Models.DataHolders;
+using System;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
 using SkiaSharp;
 using SkiaSharp;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
 
 
 namespace PixiEditor.Models.Tools
 namespace PixiEditor.Models.Tools
 {
 {
@@ -15,16 +17,17 @@ namespace PixiEditor.Models.Tools
 
 
         public bool UseDefaultUndoMethod { get; set; } = true;
         public bool UseDefaultUndoMethod { get; set; } = true;
 
 
+        public bool UseDocumentRectForUndo { get; set; } = false;
+
         private StorageBasedChange _change;
         private StorageBasedChange _change;
 
 
         public abstract void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color);
         public abstract void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color);
 
 
         public override void BeforeUse()
         public override void BeforeUse()
         {
         {
-            if (UseDefaultUndoMethod)
+            if (UseDefaultUndoMethod && !RequiresPreviewLayer)
             {
             {
-                Document doc = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
-                _change = new StorageBasedChange(doc, new[] { doc.ActiveLayer }, true);
+                InitializeStorageBasedChange(SKRectI.Empty);
             }
             }
         }
         }
 
 
@@ -32,16 +35,46 @@ namespace PixiEditor.Models.Tools
         /// Executes undo adding procedure.
         /// Executes undo adding procedure.
         /// </summary>
         /// </summary>
         /// <remarks>When overriding, set UseDefaultUndoMethod to false.</remarks>
         /// <remarks>When overriding, set UseDefaultUndoMethod to false.</remarks>
-        public override void AfterUse()
+        public override void AfterUse(SKRectI sessionRect)
         {
         {
             if (!UseDefaultUndoMethod)
             if (!UseDefaultUndoMethod)
                 return;
                 return;
+
+            if (RequiresPreviewLayer)
+            {
+                InitializeStorageBasedChange(sessionRect);
+            }
+
             var document = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
             var document = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
             var args = new object[] { _change.Document };
             var args = new object[] { _change.Document };
             document.UndoManager.AddUndoChange(_change.ToChange(UndoStorageBasedChange, args));
             document.UndoManager.AddUndoChange(_change.ToChange(UndoStorageBasedChange, args));
             _change = null;
             _change = null;
         }
         }
 
 
+        private void InitializeStorageBasedChange(SKRectI toolSessionRect)
+        {
+            Document doc = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
+            var toolSize = Toolbar.GetSetting<SizeSetting>("ToolSize");
+            SKRectI finalRect = toolSessionRect;
+            if (toolSize != null)
+            {
+                int halfSize = (int)Math.Ceiling(toolSize.Value / 2f);
+                finalRect.Inflate(halfSize, halfSize);
+            }
+
+            if (toolSessionRect.IsEmpty)
+            {
+                finalRect = SKRectI.Create(doc.ActiveLayer.OffsetX, doc.ActiveLayer.OffsetY, doc.ActiveLayer.Width, doc.ActiveLayer.Height);
+            }
+
+            if (UseDocumentRectForUndo)
+            {
+                finalRect = SKRectI.Create(0, 0, doc.Width, doc.Height);
+            }
+
+            _change = new StorageBasedChange(doc, new[] { new LayerChunk(doc.ActiveLayer, finalRect) });
+        }
+
         private void UndoStorageBasedChange(Layer[] layers, UndoLayer[] data, object[] args)
         private void UndoStorageBasedChange(Layer[] layers, UndoLayer[] data, object[] args)
         {
         {
             Document document = (Document)args[0];
             Document document = (Document)args[0];

+ 6 - 1
PixiEditor/Models/Tools/Tool.cs

@@ -4,6 +4,7 @@ using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using System.Windows.Input;
 using System.Windows.Input;
+using SkiaSharp;
 
 
 namespace PixiEditor.Models.Tools
 namespace PixiEditor.Models.Tools
 {
 {
@@ -57,7 +58,11 @@ namespace PixiEditor.Models.Tools
 
 
         public virtual void BeforeUse() { }
         public virtual void BeforeUse() { }
 
 
-        public virtual void AfterUse() { }
+        /// <summary>
+        ///     Called when the tool finished executing
+        /// </summary>
+        /// <param name="sessionRect">A rectangle which was created during session</param>
+        public virtual void AfterUse(SKRectI sessionRect) { }
 
 
         public virtual void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown) { }
         public virtual void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown) { }
     }
     }

+ 1 - 0
PixiEditor/Models/Tools/Tools/FloodFillTool.cs

@@ -17,6 +17,7 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             ActionDisplay = "Press on an area to fill it.";
             ActionDisplay = "Press on an area to fill it.";
             BitmapManager = bitmapManager;
             BitmapManager = bitmapManager;
+            UseDocumentRectForUndo = true;
         }
         }
 
 
         public override string Tooltip => "Fills area with color. (G)";
         public override string Tooltip => "Fills area with color. (G)";

+ 4 - 42
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -16,16 +16,6 @@ namespace PixiEditor.Models.Tools.Tools
 {
 {
     internal class MoveTool : BitmapOperationTool
     internal class MoveTool : BitmapOperationTool
     {
     {
-        private static readonly SKPaint maskingPaint = new()
-        {
-            BlendMode = SKBlendMode.DstIn,
-        };
-
-        private static readonly SKPaint inverseMaskingPaint = new()
-        {
-            BlendMode = SKBlendMode.DstOut,
-        };
-
         private Layer[] affectedLayers;
         private Layer[] affectedLayers;
         private Surface[] currentlyDragged;
         private Surface[] currentlyDragged;
         private Coordinates[] currentlyDraggedPositions;
         private Coordinates[] currentlyDraggedPositions;
@@ -98,7 +88,7 @@ namespace PixiEditor.Models.Tools.Tools
 
 
             if (anySelection)
             if (anySelection)
             {
             {
-                currentlyDragged = ExtractDraggedPortions(anySelection ? selLayer : null, affectedLayers);
+                currentlyDragged = BitmapUtils.ExtractSelectedPortions(selLayer, affectedLayers, true);
                 currentlyDraggedPositions = Enumerable.Repeat(new Coordinates(selLayer.OffsetX, selLayer.OffsetY), affectedLayers.Length).ToArray();
                 currentlyDraggedPositions = Enumerable.Repeat(new Coordinates(selLayer.OffsetX, selLayer.OffsetY), affectedLayers.Length).ToArray();
             }
             }
             else
             else
@@ -116,7 +106,7 @@ namespace PixiEditor.Models.Tools.Tools
             if (selLayer != null)
             if (selLayer != null)
             {
             {
                 using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
                 using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
-                combined.SkiaSurface.Canvas.DrawImage(selSnap, 0, 0, maskingPaint);
+                combined.SkiaSurface.Canvas.DrawImage(selSnap, 0, 0, Surface.MaskingPaint);
             }
             }
             return combined;
             return combined;
         }
         }
@@ -141,34 +131,6 @@ namespace PixiEditor.Models.Tools.Tools
             return (outCoords, outSurfaces);
             return (outCoords, outSurfaces);
         }
         }
 
 
-        private static Surface[] ExtractDraggedPortions(Layer selLayer, Layer[] draggedLayers)
-        {
-            using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
-            Surface[] output = new Surface[draggedLayers.Length];
-
-            int count = 0;
-            foreach (Layer layer in draggedLayers)
-            {
-                Surface portion = new Surface(selLayer.Width, selLayer.Height);
-                SKRect selLayerRect = new SKRect(0, 0, selLayer.Width, selLayer.Height);
-
-                int x = selLayer.OffsetX - layer.OffsetX;
-                int y = selLayer.OffsetY - layer.OffsetY;
-
-                using (var layerSnap = layer.LayerBitmap.SkiaSurface.Snapshot())
-                    portion.SkiaSurface.Canvas.DrawImage(layerSnap, new SKRect(x, y, x + selLayer.Width, y + selLayer.Height), selLayerRect, Surface.ReplacingPaint);
-                portion.SkiaSurface.Canvas.DrawImage(selSnap, 0, 0, maskingPaint);
-                output[count] = portion;
-                count++;
-
-                layer.LayerBitmap.SkiaSurface.Canvas.DrawImage(selSnap, new SKRect(0, 0, selLayer.Width, selLayer.Height),
-                    new SKRect(selLayer.OffsetX - layer.OffsetX, selLayer.OffsetY - layer.OffsetY, selLayer.OffsetX - layer.OffsetX + selLayer.Width, selLayer.OffsetY - layer.OffsetY + selLayer.Height),
-                    inverseMaskingPaint);
-                layer.InvokeLayerBitmapChange(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height));
-            }
-            return output;
-        }
-
         public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
             Coordinates newPos = recordedMouseMovement[^1];
             Coordinates newPos = recordedMouseMovement[^1];
@@ -188,9 +150,9 @@ namespace PixiEditor.Models.Tools.Tools
             previewLayer.InvokeLayerBitmapChange(dirtyRect);
             previewLayer.InvokeLayerBitmapChange(dirtyRect);
         }
         }
 
 
-        public override void AfterUse()
+        public override void AfterUse(SKRectI sessionRect)
         {
         {
-            base.AfterUse();
+            base.AfterUse(sessionRect);
             BitmapManager.ActiveDocument.PreviewLayer.ClearCanvas();
             BitmapManager.ActiveDocument.PreviewLayer.ClearCanvas();
 
 
             ApplySurfacesToLayers(currentlyDragged, currentlyDraggedPositions, affectedLayers, new Coordinates(lastDragDelta.X, lastDragDelta.Y));
             ApplySurfacesToLayers(currentlyDragged, currentlyDraggedPositions, affectedLayers, new Coordinates(lastDragDelta.X, lastDragDelta.Y));

+ 2 - 1
PixiEditor/Models/Tools/Tools/PenTool.cs

@@ -57,7 +57,8 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             base.BeforeUse();
             base.BeforeUse();
             changedPixelsindex = 0;
             changedPixelsindex = 0;
-            lastChangedPixels = new Coordinates[3];
+            lastChangedPixels = new Coordinates[] { new(-1, -1), new(-1, -1), new(-1, -1) };
+            lastChangedPixel = new(-1, -1);
             confirmedPixels.Clear();
             confirmedPixels.Clear();
         }
         }
 
 

+ 3 - 2
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -10,6 +10,7 @@ using PixiEditor.ViewModels;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using SkiaSharp;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
@@ -45,9 +46,9 @@ namespace PixiEditor.Models.Tools.Tools
             oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
             oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
         }
         }
 
 
-        public override void AfterUse()
+        public override void AfterUse(SKRectI sessionRect)
         {
         {
-            base.AfterUse();
+            base.AfterUse(sessionRect);
             if (ActiveSelection.SelectedPoints.Count <= 1)
             if (ActiveSelection.SelectedPoints.Count <= 1)
             {
             {
                 // If we have not selected multiple points, clear the selection
                 // If we have not selected multiple points, clear the selection

+ 17 - 0
PixiEditor/Models/Undo/LayerChunk.cs

@@ -0,0 +1,17 @@
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using SkiaSharp;
+
+namespace PixiEditor.Models.Undo;
+
+public class LayerChunk
+{
+    public Layer Layer { get; set; }
+    public SKRectI AbsoluteChunkRect { get; set; }
+
+    public LayerChunk(Layer layer, SKRectI absoluteChunkRect)
+    {
+        Layer = layer;
+        AbsoluteChunkRect = absoluteChunkRect;
+    }
+}

+ 145 - 36
PixiEditor/Models/Undo/StorageBasedChange.cs

@@ -1,47 +1,75 @@
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
+using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
+using System.Windows;
 
 
 namespace PixiEditor.Models.Undo
 namespace PixiEditor.Models.Undo
 {
 {
     /// <summary>
     /// <summary>
     ///     A class that allows to save layers on disk and load them on Undo/Redo.
     ///     A class that allows to save layers on disk and load them on Undo/Redo.
     /// </summary>
     /// </summary>
-    public class StorageBasedChange
+    public class StorageBasedChange : IDisposable
     {
     {
-        public static string DefaultUndoChangeLocation => Path.Join(Path.GetTempPath(), "PixiEditor", "UndoStack");
+        public static string DefaultUndoChangeLocation { get; } = Path.Join(Path.GetTempPath(), "PixiEditor", Guid.NewGuid().ToString(), "UndoStack");
 
 
         public string UndoChangeLocation { get; set; }
         public string UndoChangeLocation { get; set; }
 
 
         public UndoLayer[] StoredLayers { get; set; }
         public UndoLayer[] StoredLayers { get; set; }
 
 
-        private List<Guid> layersToStore;
+        private List<Guid> layersToStore = new List<Guid>();
         public Document Document { get; }
         public Document Document { get; }
 
 
         public StorageBasedChange(Document doc, IEnumerable<Layer> layers, bool saveOnStartup = true)
         public StorageBasedChange(Document doc, IEnumerable<Layer> layers, bool saveOnStartup = true)
         {
         {
             Document = doc;
             Document = doc;
-            layersToStore = layers.Select(x => x.LayerGuid).ToList();
+            Initialize(layers, DefaultUndoChangeLocation, saveOnStartup);
+        }
+
+        public StorageBasedChange(Document doc, IEnumerable<Layer> layers, string undoChangeLocation, bool saveOnStartup = true)
+        {
+            Document = doc;
+            Initialize(layers, undoChangeLocation, saveOnStartup);
+        }
+
+        public StorageBasedChange(Document doc, IEnumerable<LayerChunk> chunks, bool saveOnStartup = true)
+        {
+            Document = doc;
+            var chunkData = chunks as LayerChunk[] ?? chunks.ToArray();
+            LayerChunk[] layerChunks = new LayerChunk[chunkData.Length];
+            for (var i = 0; i < chunkData.Length; i++)
+            {
+                var chunk = chunkData[i];
+                layerChunks[i] = chunk;
+                layersToStore.Add(chunk.Layer.GuidValue);
+            }
+
             UndoChangeLocation = DefaultUndoChangeLocation;
             UndoChangeLocation = DefaultUndoChangeLocation;
-            GenerateUndoLayers();
+            GenerateUndoLayers(layerChunks);
             if (saveOnStartup)
             if (saveOnStartup)
             {
             {
                 SaveLayersOnDevice();
                 SaveLayersOnDevice();
             }
             }
         }
         }
 
 
-        public StorageBasedChange(Document doc, IEnumerable<Layer> layers, string undoChangeLocation, bool saveOnStartup = true)
+        private void Initialize(IEnumerable<Layer> layers, string undoChangeLocation, bool saveOnStartup)
         {
         {
-            Document = doc;
-            layersToStore = layers.Select(x => x.LayerGuid).ToList();
-            UndoChangeLocation = undoChangeLocation;
-            GenerateUndoLayers();
+            var layersArray = layers as Layer[] ?? layers.ToArray();
+            LayerChunk[] layerChunks = new LayerChunk[layersArray.Length];
+            for (var i = 0; i < layersArray.Length; i++)
+            {
+                var layer = layersArray[i];
+                layerChunks[i] = new LayerChunk(layer, SKRectI.Create(layer.OffsetX, layer.OffsetY, layer.Width, layer.Height));
+                layersToStore.Add(layer.GuidValue);
+            }
 
 
+            UndoChangeLocation = undoChangeLocation;
+            GenerateUndoLayers(layerChunks);
             if (saveOnStartup)
             if (saveOnStartup)
             {
             {
                 SaveLayersOnDevice();
                 SaveLayersOnDevice();
@@ -53,11 +81,23 @@ namespace PixiEditor.Models.Undo
             int i = 0;
             int i = 0;
             foreach (var layerGuid in layersToStore)
             foreach (var layerGuid in layersToStore)
             {
             {
-                Layer layer = Document.Layers.First(x => x.LayerGuid == layerGuid);
+                Layer layer = Document.Layers.First(x => x.GuidValue == layerGuid);
                 UndoLayer storedLayer = StoredLayers[i];
                 UndoLayer storedLayer = StoredLayers[i];
                 if (Directory.Exists(Path.GetDirectoryName(storedLayer.StoredPngLayerName)))
                 if (Directory.Exists(Path.GetDirectoryName(storedLayer.StoredPngLayerName)))
                 {
                 {
-                    Exporter.SaveAsGZippedBytes(storedLayer.StoredPngLayerName, layer.LayerBitmap);
+                    // Calculate absolute rect to relative rect
+                    SKRectI finalRect = SKRectI.Create(
+                        storedLayer.SerializedRect.Left - layer.OffsetX,
+                        storedLayer.SerializedRect.Top - layer.OffsetY,
+                        storedLayer.SerializedRect.Width,
+                        storedLayer.SerializedRect.Height);
+
+                    using var image = layer.LayerBitmap.SkiaSurface.Snapshot();
+                    Surface targetSizeSurface = new Surface(finalRect.Width, finalRect.Height);
+
+                    targetSizeSurface.SkiaSurface.Canvas.DrawImage(image, finalRect, SKRect.Create(0, 0, finalRect.Width, finalRect.Height), Surface.ReplacingPaint);
+
+                    Exporter.SaveAsGZippedBytes(storedLayer.StoredPngLayerName, targetSizeSurface);
                 }
                 }
 
 
                 i++;
                 i++;
@@ -79,22 +119,23 @@ namespace PixiEditor.Models.Undo
                 var bitmap = Importer.LoadFromGZippedBytes(storedLayer.StoredPngLayerName);
                 var bitmap = Importer.LoadFromGZippedBytes(storedLayer.StoredPngLayerName);
                 layers[i] = new Layer(storedLayer.Name, bitmap)
                 layers[i] = new Layer(storedLayer.Name, bitmap)
                 {
                 {
-                    Offset = new System.Windows.Thickness(storedLayer.OffsetX, storedLayer.OffsetY, 0, 0),
+                    Width = storedLayer.Width,
+                    Height = storedLayer.Height,
+                    Offset = new Thickness(storedLayer.OffsetX, storedLayer.OffsetY, 0, 0),
                     Opacity = storedLayer.Opacity,
                     Opacity = storedLayer.Opacity,
                     MaxWidth = storedLayer.MaxWidth,
                     MaxWidth = storedLayer.MaxWidth,
                     MaxHeight = storedLayer.MaxHeight,
                     MaxHeight = storedLayer.MaxHeight,
                     IsVisible = storedLayer.IsVisible,
                     IsVisible = storedLayer.IsVisible,
                     IsActive = storedLayer.IsActive,
                     IsActive = storedLayer.IsActive,
-                    Width = storedLayer.Width,
-                    Height = storedLayer.Height,
                     LayerHighlightColor = storedLayer.LayerHighlightColor
                     LayerHighlightColor = storedLayer.LayerHighlightColor
                 };
                 };
+
                 layers[i].ChangeGuid(storedLayer.LayerGuid);
                 layers[i].ChangeGuid(storedLayer.LayerGuid);
 
 
                 File.Delete(StoredLayers[i].StoredPngLayerName);
                 File.Delete(StoredLayers[i].StoredPngLayerName);
             }
             }
 
 
-            layersToStore = layers.Select(x => x.LayerGuid).ToList();
+            layersToStore = layers.Select(x => x.GuidValue).ToList();
             return layers;
             return layers;
         }
         }
 
 
@@ -121,7 +162,9 @@ namespace PixiEditor.Models.Undo
                 redoProcess(parameters);
                 redoProcess(parameters);
             };
             };
 
 
-            return new Change(finalUndoProcess, processArgs, finalRedoProcess, redoProcessParameters, description);
+            var change = new Change(finalUndoProcess, processArgs, finalRedoProcess, redoProcessParameters, description);
+            change.DisposeProcess = (_, _) => Dispose();
+            return change;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -130,21 +173,28 @@ namespace PixiEditor.Models.Undo
         /// <param name="undoRedoProcess">Process that is invoked on redo and undo.</param>
         /// <param name="undoRedoProcess">Process that is invoked on redo and undo.</param>
         /// <param name="processArgs">Custom parameters for undo and redo process.</param>
         /// <param name="processArgs">Custom parameters for undo and redo process.</param>
         /// <param name="description">Undo change description.</param>
         /// <param name="description">Undo change description.</param>
-        /// <returns>UndoManager ready Change instance.</returns>
+        /// <returns>UndoManager ready 'Change' instance.</returns>
         public Change ToChange(Action<Layer[], UndoLayer[], object[]> undoRedoProcess, object[] processArgs, string description = "")
         public Change ToChange(Action<Layer[], UndoLayer[], object[]> undoRedoProcess, object[] processArgs, string description = "")
         {
         {
             Action<object[]> finalProcess = processParameters =>
             Action<object[]> finalProcess = processParameters =>
             {
             {
-
                 Layer[] layers = LoadLayersFromDevice();
                 Layer[] layers = LoadLayersFromDevice();
-                GenerateUndoLayers();
+                LayerChunk[] chunks = new LayerChunk[layers.Length];
+                for (int i = 0; i < layers.Length; i++)
+                {
+                    chunks[i] = new LayerChunk(layers[i], StoredLayers[i].SerializedRect);
+                }
+
+                GenerateUndoLayers(chunks);
 
 
                 SaveLayersOnDevice();
                 SaveLayersOnDevice();
 
 
                 undoRedoProcess(layers, StoredLayers, processParameters);
                 undoRedoProcess(layers, StoredLayers, processParameters);
             };
             };
 
 
-            return new Change(finalProcess, processArgs, finalProcess, processArgs, description);
+            var change = new Change(finalProcess, processArgs, finalProcess, processArgs, description);
+            change.DisposeProcess = (_, _) => Dispose();
+            return change;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -169,7 +219,9 @@ namespace PixiEditor.Models.Undo
                 redoProcess(parameters);
                 redoProcess(parameters);
             };
             };
 
 
-            return new Change(finalUndoProcess, null, finalRedoProcess, redoProcessParameters, description);
+            var change = new Change(finalUndoProcess, null, finalRedoProcess, redoProcessParameters, description);
+            change.DisposeProcess = (_, _) => Dispose();
+            return change;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -194,7 +246,9 @@ namespace PixiEditor.Models.Undo
                 redoProcess(layers, StoredLayers);
                 redoProcess(layers, StoredLayers);
             };
             };
 
 
-            return new Change(finalUndoProcess, undoProcessParameters, finalRedoProcess, null, description);
+            var change = new Change(finalUndoProcess, undoProcessParameters, finalRedoProcess, null, description);
+            change.DisposeProcess = (_, _) => Dispose();
+            return change;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -220,34 +274,35 @@ namespace PixiEditor.Models.Undo
                 redoProcess(layers, StoredLayers, parameters);
                 redoProcess(layers, StoredLayers, parameters);
             };
             };
 
 
-            return new Change(finalUndoProcess, undoProcessParameters, finalRedoProcess, redoProcessArgs, description);
+            var change = new Change(finalUndoProcess, undoProcessParameters, finalRedoProcess, redoProcessArgs, description);
+            change.DisposeProcess = (_, _) => Dispose();
+            return change;
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Generates UndoLayer[] StoredLayers data.
         /// Generates UndoLayer[] StoredLayers data.
         /// </summary>
         /// </summary>
-        private void GenerateUndoLayers()
+        private void GenerateUndoLayers(LayerChunk[] chunks)
         {
         {
             StoredLayers = new UndoLayer[layersToStore.Count];
             StoredLayers = new UndoLayer[layersToStore.Count];
             int i = 0;
             int i = 0;
             foreach (var layerGuid in layersToStore)
             foreach (var layerGuid in layersToStore)
             {
             {
-                Layer layer = Document.Layers.First(x => x.LayerGuid == layerGuid);
+                Layer layer = Document.Layers.First(x => x.GuidValue == layerGuid);
                 if (!Document.Layers.Contains(layer))
                 if (!Document.Layers.Contains(layer))
                 {
                 {
                     throw new ArgumentException("Provided document doesn't contain selected layer");
                     throw new ArgumentException("Provided document doesn't contain selected layer");
                 }
                 }
 
 
-                layer.ClipCanvas();
-
                 int index = Document.Layers.IndexOf(layer);
                 int index = Document.Layers.IndexOf(layer);
-                string pngName = layer.Name + Guid.NewGuid().ToString();
+                string fileName = layer.Name + Guid.NewGuid();
                 StoredLayers[i] = new UndoLayer(
                 StoredLayers[i] = new UndoLayer(
                     Path.Join(
                     Path.Join(
                         UndoChangeLocation,
                         UndoChangeLocation,
-                        Convert.ToBase64String(Encoding.UTF8.GetBytes(pngName)) + ".png"),
+                        Convert.ToBase64String(Encoding.UTF8.GetBytes(fileName)) + ".undoimg"),
                     layer,
                     layer,
-                    index);
+                    index,
+                    chunks[i].AbsoluteChunkRect);
                 i++;
                 i++;
             }
             }
         }
         }
@@ -259,17 +314,71 @@ namespace PixiEditor.Models.Undo
                 for (int i = 0; i < layers.Length; i++)
                 for (int i = 0; i < layers.Length; i++)
                 {
                 {
                     Layer layer = layers[i];
                     Layer layer = layers[i];
+                    UndoLayer layerData = data[i];
+                    var foundLayer = document.Layers.FirstOrDefault(x => x.GuidValue == layerData.LayerGuid);
 
 
-                    document.RemoveLayer(data[i].LayerIndex, false);
-                    document.Layers.Insert(data[i].LayerIndex, layer);
+                    if (foundLayer != null)
+                    {
+                        ApplyChunkToLayer(foundLayer, layerData, layer.LayerBitmap);
+                    }
+                    else
+                    {
+                        document.RemoveLayer(layerData.LayerIndex, false);
+                        document.Layers.Insert(layerData.LayerIndex, layer);
+                    }
 
 
-                    if (data[i].IsActive)
+                    if (layerData.IsActive)
                     {
                     {
-                        document.SetMainActiveLayer(data[i].LayerIndex);
+                        document.SetMainActiveLayer(layerData.LayerIndex);
                     }
                     }
                 }
                 }
-
             }
             }
         }
         }
+
+        private static void ApplyChunkToLayer(Layer layer, UndoLayer layerData, Surface chunk)
+        {
+            bool widthBigger = layer.Width < chunk.Width;
+            bool heightBigger = layer.Height < chunk.Height;
+            int targetWidth = widthBigger ? chunk.Width : layer.Width;
+            int targetHeight = heightBigger ? chunk.Height : layer.Height;
+
+            int offsetDiffX = layerData.OffsetX - layer.OffsetX;
+            int offsetDiffY = layerData.OffsetY - layer.OffsetY;
+
+            int targetOffsetX = layerData.OffsetX == 0 && widthBigger ? layerData.SerializedRect.Left : layerData.OffsetX;
+            int targetOffsetY = layerData.OffsetY == 0 && heightBigger ? layerData.SerializedRect.Top : layerData.OffsetY;
+
+            Surface targetSizeSurface = new Surface(targetWidth, targetHeight);
+            using var foundLayerSnapshot = layer.LayerBitmap.SkiaSurface.Snapshot();
+            targetSizeSurface.SkiaSurface.Canvas.DrawImage(
+                foundLayerSnapshot,
+                SKRect.Create(offsetDiffX, offsetDiffY, layer.Width, layer.Height),
+                SKRect.Create(0, 0, targetWidth, targetHeight),
+                Surface.ReplacingPaint);
+
+            layer.Offset = new Thickness(targetOffsetX, targetOffsetY, 0, 0);
+
+            SKRect finalRect = SKRect.Create(
+                layerData.SerializedRect.Left - layer.OffsetX,
+                layerData.SerializedRect.Top - layer.OffsetY,
+                layerData.SerializedRect.Width,
+                layerData.SerializedRect.Height);
+
+            using var snapshot = chunk.SkiaSurface.Snapshot();
+
+            targetSizeSurface.SkiaSurface.Canvas.DrawImage(
+                snapshot,
+                finalRect,
+                Surface.ReplacingPaint);
+
+            layer.LayerBitmap = targetSizeSurface;
+        }
+
+        public void Dispose()
+        {
+            var layers = LoadLayersFromDevice();
+            foreach (var layer in layers)
+                layer.LayerBitmap.Dispose();
+        }
     }
     }
 }
 }

+ 7 - 2
PixiEditor/Models/Undo/UndoLayer.cs

@@ -1,5 +1,7 @@
 using System;
 using System;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using SkiaSharp;
 
 
 namespace PixiEditor.Models.Undo
 namespace PixiEditor.Models.Undo
 {
 {
@@ -34,7 +36,9 @@ namespace PixiEditor.Models.Undo
 
 
         public float Opacity { get; set; }
         public float Opacity { get; set; }
 
 
-        public UndoLayer(string storedPngLayerName, Layer layer, int layerIndex)
+        public SKRectI SerializedRect { get; set; }
+
+        public UndoLayer(string storedPngLayerName, Layer layer, int layerIndex, SKRectI serializedRect)
         {
         {
             StoredPngLayerName = storedPngLayerName;
             StoredPngLayerName = storedPngLayerName;
             LayerIndex = layerIndex;
             LayerIndex = layerIndex;
@@ -48,8 +52,9 @@ namespace PixiEditor.Models.Undo
             OffsetY = layer.OffsetY;
             OffsetY = layer.OffsetY;
             Opacity = layer.Opacity;
             Opacity = layer.Opacity;
             IsActive = layer.IsActive;
             IsActive = layer.IsActive;
-            LayerGuid = layer.LayerGuid;
+            LayerGuid = layer.GuidValue;
             LayerHighlightColor = layer.LayerHighlightColor;
             LayerHighlightColor = layer.LayerHighlightColor;
+            SerializedRect = serializedRect;
         }
         }
     }
     }
 }
 }

+ 2 - 2
PixiEditor/Properties/AssemblyInfo.cs

@@ -50,5 +50,5 @@ using System.Windows;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.1.6.5")]
-[assembly: AssemblyFileVersion("0.1.6.5")]
+[assembly: AssemblyVersion("0.1.6.6")]
+[assembly: AssemblyFileVersion("0.1.6.6")]

+ 8 - 8
PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -92,7 +92,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
             else if (parameter is LayerGroup group)
             else if (parameter is LayerGroup group)
             {
             {
-                DeleteGroup(group.GroupGuid);
+                DeleteGroup(group.GuidValue);
             }
             }
             else if (parameter is LayerGroupControl groupControl)
             else if (parameter is LayerGroupControl groupControl)
             {
             {
@@ -137,7 +137,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 var lastGroups = doc.LayerStructure.CloneGroups();
                 var lastGroups = doc.LayerStructure.CloneGroups();
                 if (parameter is Layer or LayerStructureItemContainer)
                 if (parameter is Layer or LayerStructureItemContainer)
                 {
                 {
-                    GuidStructureItem group = doc.LayerStructure.AddNewGroup($"{doc.ActiveLayer.Name} Group", doc.ActiveLayer.LayerGuid);
+                    GuidStructureItem group = doc.LayerStructure.AddNewGroup($"{doc.ActiveLayer.Name} Group", doc.ActiveLayer.GuidValue);
 
 
                     Owner.BitmapManager.ActiveDocument.LayerStructure.ExpandParentGroups(group);
                     Owner.BitmapManager.ActiveDocument.LayerStructure.ExpandParentGroups(group);
                 }
                 }
@@ -181,7 +181,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
             if (doc.Layers.Count > 1)
             if (doc.Layers.Count > 1)
             {
             {
-                doc.MoveLayerInStructure(doc.Layers[^1].LayerGuid, lastActiveLayerGuid, true);
+                doc.MoveLayerInStructure(doc.Layers[^1].GuidValue, lastActiveLayerGuid, true);
                 Guid? parent = parameter is Layer or LayerStructureItemContainer ? activeLayerParent?.GroupGuid : activeLayerParent.Parent?.GroupGuid;
                 Guid? parent = parameter is Layer or LayerStructureItemContainer ? activeLayerParent?.GroupGuid : activeLayerParent.Parent?.GroupGuid;
                 doc.LayerStructure.AssignParent(doc.ActiveLayerGuid, parent);
                 doc.LayerStructure.AssignParent(doc.ActiveLayerGuid, parent);
                 doc.AddLayerStructureToUndo(oldGroups);
                 doc.AddLayerStructureToUndo(oldGroups);
@@ -265,16 +265,16 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         public void MoveLayerToFront(object parameter)
         public void MoveLayerToFront(object parameter)
         {
         {
             int oldIndex = (int)parameter;
             int oldIndex = (int)parameter;
-            Guid layerToMove = Owner.BitmapManager.ActiveDocument.Layers[oldIndex].LayerGuid;
-            Guid referenceLayer = Owner.BitmapManager.ActiveDocument.Layers[oldIndex + 1].LayerGuid;
+            Guid layerToMove = Owner.BitmapManager.ActiveDocument.Layers[oldIndex].GuidValue;
+            Guid referenceLayer = Owner.BitmapManager.ActiveDocument.Layers[oldIndex + 1].GuidValue;
             Owner.BitmapManager.ActiveDocument.MoveLayerInStructure(layerToMove, referenceLayer, true);
             Owner.BitmapManager.ActiveDocument.MoveLayerInStructure(layerToMove, referenceLayer, true);
         }
         }
 
 
         public void MoveLayerToBack(object parameter)
         public void MoveLayerToBack(object parameter)
         {
         {
             int oldIndex = (int)parameter;
             int oldIndex = (int)parameter;
-            Guid layerToMove = Owner.BitmapManager.ActiveDocument.Layers[oldIndex].LayerGuid;
-            Guid referenceLayer = Owner.BitmapManager.ActiveDocument.Layers[oldIndex - 1].LayerGuid;
+            Guid layerToMove = Owner.BitmapManager.ActiveDocument.Layers[oldIndex].GuidValue;
+            Guid referenceLayer = Owner.BitmapManager.ActiveDocument.Layers[oldIndex - 1].GuidValue;
             Owner.BitmapManager.ActiveDocument.MoveLayerInStructure(layerToMove, referenceLayer, false);
             Owner.BitmapManager.ActiveDocument.MoveLayerInStructure(layerToMove, referenceLayer, false);
         }
         }
 
 
@@ -349,7 +349,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
             else if (parameter is Layer || parameter is LayerStructureItemContainer)
             else if (parameter is Layer || parameter is LayerStructureItemContainer)
             {
             {
-                Guid layerGuid = parameter is Layer layer ? layer.LayerGuid : ((LayerStructureItemContainer)parameter).Layer.LayerGuid;
+                Guid layerGuid = parameter is Layer layer ? layer.GuidValue : ((LayerStructureItemContainer)parameter).Layer.GuidValue;
                 var group = Owner.BitmapManager.ActiveDocument.LayerStructure.GetGroupByLayer(layerGuid);
                 var group = Owner.BitmapManager.ActiveDocument.LayerStructure.GetGroupByLayer(layerGuid);
                 if (group != null)
                 if (group != null)
                 {
                 {

+ 16 - 7
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
+using System;
 using System.IO;
 using System.IO;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
@@ -10,17 +11,17 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public RelayCommand RedoCommand { get; set; }
         public RelayCommand RedoCommand { get; set; }
 
 
+        public event EventHandler UndoRedoCalled;
+
         public UndoViewModel(ViewModelMain owner)
         public UndoViewModel(ViewModelMain owner)
             : base(owner)
             : base(owner)
         {
         {
             UndoCommand = new RelayCommand(Undo, CanUndo);
             UndoCommand = new RelayCommand(Undo, CanUndo);
             RedoCommand = new RelayCommand(Redo, CanRedo);
             RedoCommand = new RelayCommand(Redo, CanRedo);
-            if (!Directory.Exists(StorageBasedChange.DefaultUndoChangeLocation))
-            {
-                Directory.CreateDirectory(StorageBasedChange.DefaultUndoChangeLocation);
-            }
 
 
-            ClearUndoTempDirectory();
+            var result = Directory.CreateDirectory(StorageBasedChange.DefaultUndoChangeLocation);
+
+            //ClearUndoTempDirectory();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -29,7 +30,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         /// <param name="parameter">CommandProperty.</param>
         /// <param name="parameter">CommandProperty.</param>
         public void Redo(object parameter)
         public void Redo(object parameter)
         {
         {
-            Owner.BitmapManager.ActiveDocument.UndoManager.Redo();
+            UndoRedoCalled?.Invoke(this, EventArgs.Empty);
+
+            //sometimes CanRedo gets changed after UndoRedoCalled invoke, so check again (normally this is checked by the relaycommand)
+            if (CanRedo(null))
+                Owner.BitmapManager.ActiveDocument.UndoManager.Redo();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -38,7 +43,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         /// <param name="parameter">CommandParameter.</param>
         /// <param name="parameter">CommandParameter.</param>
         public void Undo(object parameter)
         public void Undo(object parameter)
         {
         {
-            Owner.BitmapManager.ActiveDocument.UndoManager.Undo();
+            UndoRedoCalled?.Invoke(this, EventArgs.Empty);
+
+            //sometimes CanUndo gets changed after UndoRedoCalled invoke, so check again (normally this is checked by the relaycommand)
+            if (CanUndo(null))
+                Owner.BitmapManager.ActiveDocument.UndoManager.Undo();
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 6 - 1
PixiEditor/ViewModels/ViewModelMain.cs

@@ -325,12 +325,17 @@ namespace PixiEditor.ViewModels
                 if (result == ConfirmationType.Yes)
                 if (result == ConfirmationType.Yes)
                 {
                 {
                     FileSubViewModel.SaveDocument(false);
                     FileSubViewModel.SaveDocument(false);
+                    //cancel was pressed in the save file dialog
+                    if (!BitmapManager.ActiveDocument.ChangesSaved)
+                        return false;
                 }
                 }
             }
             }
 
 
             if (result != ConfirmationType.Canceled)
             if (result != ConfirmationType.Canceled)
             {
             {
-                BitmapManager.Documents.Remove(BitmapManager.ActiveDocument);
+                var doc = BitmapManager.ActiveDocument;
+                BitmapManager.Documents.Remove(doc);
+                doc.Dispose();
 
 
                 return true;
                 return true;
             }
             }

+ 1 - 2
PixiEditor/Views/MainWindow.xaml

@@ -31,7 +31,6 @@
             <converters:IsSpecifiedTypeConverter SpecifiedType="{x:Type tools:ZoomTool}" x:Key="IsZoomToolConverter"/>
             <converters:IsSpecifiedTypeConverter SpecifiedType="{x:Type tools:ZoomTool}" x:Key="IsZoomToolConverter"/>
             <converters:IsSpecifiedTypeConverter SpecifiedType="{x:Type tools:MoveViewportTool}" x:Key="IsMoveViewportToolConverter"/>
             <converters:IsSpecifiedTypeConverter SpecifiedType="{x:Type tools:MoveViewportTool}" x:Key="IsMoveViewportToolConverter"/>
             <converters:SKColorToMediaColorConverter x:Key="SKColorToMediaColorConverter"/>
             <converters:SKColorToMediaColorConverter x:Key="SKColorToMediaColorConverter"/>
-            <converters:DockingManagerActiveContentConverter x:Key="DockingManagerActiveContentConverter"/>
             <ResourceDictionary.MergedDictionaries>
             <ResourceDictionary.MergedDictionaries>
                 <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
                 <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
             </ResourceDictionary.MergedDictionaries>
             </ResourceDictionary.MergedDictionaries>
@@ -258,7 +257,7 @@
         </StackPanel>
         </StackPanel>
         <Grid Grid.Column="1" Grid.Row="2" Background="#303030" >
         <Grid Grid.Column="1" Grid.Row="2" Background="#303030" >
             <Grid AllowDrop="True" Drop="MainWindow_Drop">
             <Grid AllowDrop="True" Drop="MainWindow_Drop">
-                <DockingManager ActiveContent="{Binding BitmapManager.ActiveDocument, Mode=TwoWay, Converter={StaticResource DockingManagerActiveContentConverter}}"
+                <DockingManager ActiveContent="{Binding BitmapManager.ActiveWindow, Mode=TwoWay}"
                                            DocumentsSource="{Binding BitmapManager.Documents}">
                                            DocumentsSource="{Binding BitmapManager.Documents}">
                     <DockingManager.Theme>
                     <DockingManager.Theme>
                         <avalonDockTheme:PixiEditorDockTheme />
                         <avalonDockTheme:PixiEditorDockTheme />

+ 1 - 2
PixiEditor/Views/MainWindow.xaml.cs

@@ -4,7 +4,6 @@ using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
-using PixiEditor.Views.UserControls;
 using System;
 using System;
 using System.ComponentModel;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics;
@@ -185,4 +184,4 @@ namespace PixiEditor
             }
             }
         }
         }
     }
     }
-}
+}

+ 4 - 4
PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml

@@ -29,11 +29,11 @@
                 <CheckBox Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
                 <CheckBox Style="{StaticResource ImageCheckBox}" VerticalAlignment="Center"
                       IsThreeState="False" HorizontalAlignment="Center" Click="CheckBox_Checked"
                       IsThreeState="False" HorizontalAlignment="Center" Click="CheckBox_Checked"
                       IsChecked="{Binding Path=IsVisibleUndoTriggerable, ElementName=groupControl}" Grid.Column="0" Height="16"/>
                       IsChecked="{Binding Path=IsVisibleUndoTriggerable, ElementName=groupControl}" Grid.Column="0" Height="16"/>
-                
+
                 <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Left">
                 <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Left">
                     <Rectangle Width="{Binding Path=(helpers:TreeViewItemHelper.Indent).Value, Mode=OneWay, RelativeSource={RelativeSource AncestorType=ItemsPresenter}}" Fill="Transparent" StrokeThickness="0"/>
                     <Rectangle Width="{Binding Path=(helpers:TreeViewItemHelper.Indent).Value, Mode=OneWay, RelativeSource={RelativeSource AncestorType=ItemsPresenter}}" Fill="Transparent" StrokeThickness="0"/>
-                
-                <StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left">
+
+                    <StackPanel Grid.Row="1" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left">
                         <Border Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
                         <Border Width="30" Height="30" BorderThickness="1" BorderBrush="Black" Background="{StaticResource MainColor}"
                            Margin="5, 0, 10, 0">
                            Margin="5, 0, 10, 0">
                             <Image Source="{Binding PreviewImage, ElementName=groupControl}" Stretch="Uniform" Width="20" Height="20" 
                             <Image Source="{Binding PreviewImage, ElementName=groupControl}" Stretch="Uniform" Width="20" Height="20" 
@@ -44,7 +44,7 @@
                     VerticalAlignment="Center"
                     VerticalAlignment="Center"
                     IsEditing="{Binding GroupData.IsRenaming, ElementName=groupControl, Mode=TwoWay}"
                     IsEditing="{Binding GroupData.IsRenaming, ElementName=groupControl, Mode=TwoWay}"
                     Text="{Binding GroupData.Name, ElementName=groupControl, Mode=TwoWay}" />
                     Text="{Binding GroupData.Name, ElementName=groupControl, Mode=TwoWay}" />
-                </StackPanel>
+                    </StackPanel>
                     <Image Source="/Images/Folder.png" Height="20" Margin="0,0,10,0" HorizontalAlignment="Right"/>
                     <Image Source="/Images/Folder.png" Height="20" Margin="0,0,10,0" HorizontalAlignment="Right"/>
                 </StackPanel>
                 </StackPanel>
             </Grid>
             </Grid>

+ 7 - 7
PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs

@@ -170,7 +170,7 @@ namespace PixiEditor.Views.UserControls.Layers
         private void HandleLayerDrop(IDataObject dataObj, bool above, Guid referenceLayer, bool putItInside) // step brother
         private void HandleLayerDrop(IDataObject dataObj, bool above, Guid referenceLayer, bool putItInside) // step brother
         {
         {
             var data = (LayerStructureItemContainer)dataObj.GetData(LayerContainerDataName);
             var data = (LayerStructureItemContainer)dataObj.GetData(LayerContainerDataName);
-            Guid group = data.Layer.LayerGuid;
+            Guid group = data.Layer.GuidValue;
 
 
             data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.MoveLayerInStructure(group, referenceLayer, above);
             data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument.MoveLayerInStructure(group, referenceLayer, above);
 
 
@@ -193,7 +193,7 @@ namespace PixiEditor.Views.UserControls.Layers
 
 
             int modifier = above ? 1 : 0;
             int modifier = above ? 1 : 0;
 
 
-            Layer layer = document.Layers.First(x => x.LayerGuid == referenceLayer);
+            Layer layer = document.Layers.First(x => x.GuidValue == referenceLayer);
             int indexOfReferenceLayer = Math.Clamp(document.Layers.IndexOf(layer) + modifier, 0, document.Layers.Count);
             int indexOfReferenceLayer = Math.Clamp(document.Layers.IndexOf(layer) + modifier, 0, document.Layers.Count);
             MoveGroupWithTempLayer(above, document, group, indexOfReferenceLayer, putItInside);
             MoveGroupWithTempLayer(above, document, group, indexOfReferenceLayer, putItInside);
         }
         }
@@ -206,9 +206,9 @@ namespace PixiEditor.Views.UserControls.Layers
 
 
             Guid? refGuid = putItInside ? GroupData?.GroupGuid : GroupData?.Parent?.GroupGuid;
             Guid? refGuid = putItInside ? GroupData?.GroupGuid : GroupData?.Parent?.GroupGuid;
 
 
-            document.LayerStructure.AssignParent(tempLayer.LayerGuid, refGuid);
-            document.MoveGroupInStructure(group, tempLayer.LayerGuid, above);
-            document.LayerStructure.AssignParent(tempLayer.LayerGuid, null);
+            document.LayerStructure.AssignParent(tempLayer.GuidValue, refGuid);
+            document.MoveGroupInStructure(group, tempLayer.GuidValue, above);
+            document.LayerStructure.AssignParent(tempLayer.GuidValue, null);
             document.RemoveLayer(tempLayer, false);
             document.RemoveLayer(tempLayer, false);
         }
         }
 
 
@@ -247,8 +247,8 @@ namespace PixiEditor.Views.UserControls.Layers
         private void Border_MouseDown(object sender, MouseButtonEventArgs e)
         private void Border_MouseDown(object sender, MouseButtonEventArgs e)
         {
         {
             var doc = LayersViewModel.Owner.BitmapManager.ActiveDocument;
             var doc = LayersViewModel.Owner.BitmapManager.ActiveDocument;
-            var layer = doc.Layers.First(x => x.LayerGuid == GroupData.EndLayerGuid);
-            if (doc.ActiveLayerGuid != layer.LayerGuid)
+            var layer = doc.Layers.First(x => x.GuidValue == GroupData.EndLayerGuid);
+            if (doc.ActiveLayerGuid != layer.GuidValue)
             {
             {
                 doc.SetMainActiveLayer(doc.Layers.IndexOf(layer));
                 doc.SetMainActiveLayer(doc.Layers.IndexOf(layer));
             }
             }

+ 1 - 1
PixiEditor/Views/UserControls/Layers/LayerItem.xaml.cs

@@ -158,7 +158,7 @@ namespace PixiEditor.Views.UserControls.Layers
             if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
             if (e.Data.GetDataPresent(LayerGroupControl.LayerContainerDataName))
             {
             {
                 var data = (LayerStructureItemContainer)e.Data.GetData(LayerGroupControl.LayerContainerDataName);
                 var data = (LayerStructureItemContainer)e.Data.GetData(LayerGroupControl.LayerContainerDataName);
-                Guid layer = data.Layer.LayerGuid;
+                Guid layer = data.Layer.GuidValue;
                 var doc = data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
                 var doc = data.LayerCommandsViewModel.Owner.BitmapManager.ActiveDocument;
 
 
                 doc.MoveLayerInStructure(layer, LayerGuid, above);
                 doc.MoveLayerInStructure(layer, LayerGuid, above);

+ 1 - 1
PixiEditor/Views/UserControls/Layers/LayerStructureItemContainer.xaml

@@ -12,7 +12,7 @@
                                        IsActive="{Binding IsActive, Mode=TwoWay}"
                                        IsActive="{Binding IsActive, Mode=TwoWay}"
                                        IsRenaming="{Binding IsRenaming, Mode=TwoWay}"
                                        IsRenaming="{Binding IsRenaming, Mode=TwoWay}"
                                        LayerForPreview="{Binding}"
                                        LayerForPreview="{Binding}"
-                                       LayerGuid="{Binding LayerGuid}"
+                                       LayerGuid="{Binding GuidValue}"
                                        LayerColor="{Binding LayerHighlightColor}"
                                        LayerColor="{Binding LayerHighlightColor}"
                                        LayerIndex="{Binding ContainerIndex, ElementName=layerStructureContainer}"
                                        LayerIndex="{Binding ContainerIndex, ElementName=layerStructureContainer}"
                                       MoveToBackCommand="{Binding LayerCommandsViewModel.MoveToBackCommand, ElementName=layerStructureContainer}"
                                       MoveToBackCommand="{Binding LayerCommandsViewModel.MoveToBackCommand, ElementName=layerStructureContainer}"

+ 2 - 2
PixiEditor/Views/UserControls/Layers/LayersManager.xaml

@@ -59,7 +59,7 @@
         </DockPanel>
         </DockPanel>
         <Separator Grid.Row="1" Margin="0,-12, 0, 0" BorderBrush="{StaticResource DarkerAccentColor}" BorderThickness="2" />
         <Separator Grid.Row="1" Margin="0,-12, 0, 0" BorderBrush="{StaticResource DarkerAccentColor}" BorderThickness="2" />
         <DockPanel LastChildFill="True" Grid.Row="2" Margin="0, -12, 0, 0">
         <DockPanel LastChildFill="True" Grid.Row="2" Margin="0, -12, 0, 0">
-            <TreeView DockPanel.Dock="Top" Name="treeView" ItemsSource="{Binding LayerTreeRoot, ElementName=layersManager}"  SelectedItemChanged="TreeView_SelectedItemChanged">
+            <TreeView DockPanel.Dock="Top" Name="treeView" ItemsSource="{Binding CachedLayerTreeRoot, ElementName=layersManager}"  SelectedItemChanged="TreeView_SelectedItemChanged">
                 <TreeView.ItemsPanel>
                 <TreeView.ItemsPanel>
                     <ItemsPanelTemplate>
                     <ItemsPanelTemplate>
                         <ui:ReversedOrderStackPanel/>
                         <ui:ReversedOrderStackPanel/>
@@ -71,7 +71,7 @@
                                              IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" 
                                              IsVisibleUndoTriggerable="{Binding StructureData.IsVisible}" 
                                              GroupOpacity="{Binding StructureData.Opacity}"
                                              GroupOpacity="{Binding StructureData.Opacity}"
                                              LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" 
                                              LayersViewModel="{Binding LayerCommandsViewModel, ElementName=layersManager}" 
-                                             GroupGuid="{Binding GroupGuid}" 
+                                             GroupGuid="{Binding GuidValue}" 
                                              GroupData="{Binding StructureData}"
                                              GroupData="{Binding StructureData}"
                                              MouseMove="LayerGroup_MouseMove"/>
                                              MouseMove="LayerGroup_MouseMove"/>
                     </HierarchicalDataTemplate>
                     </HierarchicalDataTemplate>

+ 185 - 13
PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -4,6 +4,7 @@ using PixiEditor.Models.Layers;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using System;
 using System;
+using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
@@ -23,21 +24,34 @@ namespace PixiEditor.Views.UserControls.Layers
         }
         }
 
 
         public static readonly DependencyProperty SelectedItemProperty =
         public static readonly DependencyProperty SelectedItemProperty =
-            DependencyProperty.Register("SelectedItem", typeof(object), typeof(LayersManager), new PropertyMetadata(0));
+            DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(LayersManager), new PropertyMetadata(0));
 
 
-
-        public ObservableCollection<object> LayerTreeRoot
+        public ObservableCollection<IHasGuid> LayerTreeRoot
         {
         {
-            get { return (ObservableCollection<object>)GetValue(LayerTreeRootProperty); }
+            get { return (ObservableCollection<IHasGuid>)GetValue(LayerTreeRootProperty); }
             set { SetValue(LayerTreeRootProperty, value); }
             set { SetValue(LayerTreeRootProperty, value); }
         }
         }
 
 
         public static readonly DependencyProperty LayerTreeRootProperty =
         public static readonly DependencyProperty LayerTreeRootProperty =
             DependencyProperty.Register(
             DependencyProperty.Register(
-                "LayerTreeRoot",
-                typeof(ObservableCollection<object>),
+                nameof(LayerTreeRoot),
+                typeof(ObservableCollection<IHasGuid>),
                 typeof(LayersManager),
                 typeof(LayersManager),
-                new PropertyMetadata(default(ObservableCollection<object>)));
+                new PropertyMetadata(default(ObservableCollection<IHasGuid>), LayerTreeRootChanged));
+
+        public ObservableCollection<IHasGuid> CachedLayerTreeRoot
+        {
+            get { return (ObservableCollection<IHasGuid>)GetValue(CachedLayerTreeRootProperty); }
+            set { SetValue(CachedLayerTreeRootProperty, value); }
+        }
+
+        public static readonly DependencyProperty CachedLayerTreeRootProperty =
+            DependencyProperty.Register(
+                nameof(CachedLayerTreeRoot),
+                typeof(ObservableCollection<IHasGuid>),
+                typeof(LayersManager),
+                new PropertyMetadata(default(ObservableCollection<IHasGuid>)));
+
         public LayersViewModel LayerCommandsViewModel
         public LayersViewModel LayerCommandsViewModel
         {
         {
             get { return (LayersViewModel)GetValue(LayerCommandsViewModelProperty); }
             get { return (LayersViewModel)GetValue(LayerCommandsViewModelProperty); }
@@ -45,7 +59,7 @@ namespace PixiEditor.Views.UserControls.Layers
         }
         }
 
 
         public static readonly DependencyProperty LayerCommandsViewModelProperty =
         public static readonly DependencyProperty LayerCommandsViewModelProperty =
-            DependencyProperty.Register("LayerCommandsViewModel", typeof(LayersViewModel), typeof(LayersManager), new PropertyMetadata(default(LayersViewModel), ViewModelChanged));
+            DependencyProperty.Register(nameof(LayerCommandsViewModel), typeof(LayersViewModel), typeof(LayersManager), new PropertyMetadata(default(LayersViewModel), ViewModelChanged));
 
 
         public bool OpacityInputEnabled
         public bool OpacityInputEnabled
         {
         {
@@ -54,13 +68,171 @@ namespace PixiEditor.Views.UserControls.Layers
         }
         }
 
 
         public static readonly DependencyProperty OpacityInputEnabledProperty =
         public static readonly DependencyProperty OpacityInputEnabledProperty =
-            DependencyProperty.Register("OpacityInputEnabled", typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
+            DependencyProperty.Register(nameof(OpacityInputEnabled), typeof(bool), typeof(LayersManager), new PropertyMetadata(false));
 
 
         public LayersManager()
         public LayersManager()
         {
         {
             InitializeComponent();
             InitializeComponent();
         }
         }
 
 
+        private static void LayerTreeRootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            var manager = (LayersManager)d;
+            var newRoot = (ObservableCollection<IHasGuid>)e.NewValue;
+
+            manager.CachedLayerTreeRoot = newRoot;
+            return;
+            //layer tree caching goes after than and disabled for now
+
+            if (manager.CachedLayerTreeRoot == null || newRoot == null)
+            {
+                manager.CachedLayerTreeRoot = newRoot;
+                return;
+            }
+
+            if (object.ReferenceEquals(manager.CachedLayerTreeRoot, newRoot))
+                return;
+
+            UpdateCachedTree(manager.CachedLayerTreeRoot, newRoot);
+        }
+
+        private static void UpdateCachedTree(IList<IHasGuid> tree, IList<IHasGuid> newTree)
+        {
+            var (lcs1, lcs2) = LongestCommonSubsequence(tree, newTree);
+            UpdateListUsingLCS(tree, newTree, lcs1);
+
+            bool suc = AreCollectionsTheSame(tree, newTree);
+
+
+            for (int i = 0; i < lcs1.Count; i++)
+            {
+                var entry = lcs1[i];
+                if (entry is LayerGroup group)
+                {
+                    var corrGroup = (LayerGroup)lcs2[i];
+                    UpdateCachedTree(group.Items, corrGroup.Items);
+                    group.StructureData = corrGroup.StructureData;
+                }
+            }
+        }
+
+        private static void UpdateListUsingLCS(IList<IHasGuid> list, IList<IHasGuid> newList, IList<IHasGuid> lcs)
+        {
+            int oldI = 0;
+            int newI = 0;
+            for (int i = 0; i < lcs.Count; i++)
+            {
+                Guid curLcsEntry = lcs[i].GuidValue;
+
+                while (true)
+                {
+                    if (curLcsEntry != list[oldI].GuidValue && curLcsEntry != newList[newI].GuidValue)
+                    {
+                        list[oldI] = newList[newI];
+                        oldI++;
+                        newI++;
+                    }
+                    else if (curLcsEntry == list[oldI].GuidValue && curLcsEntry != newList[newI].GuidValue)
+                    {
+                        list.Insert(oldI, newList[newI]);
+                        oldI++;
+                        newI++;
+                    }
+                    else if (curLcsEntry != list[oldI].GuidValue && curLcsEntry == newList[newI].GuidValue)
+                    {
+                        list.RemoveAt(oldI);
+                    }
+                    else
+                    {
+                        oldI++;
+                        newI++;
+                        break;
+                    }
+                }
+            }
+            while (list.Count > oldI)
+                list.RemoveAt(list.Count - 1);
+            for (; newI < newList.Count; newI++)
+                list.Add(newList[newI]);
+        }
+
+        private static bool AreCollectionsTheSame(IList<IHasGuid> coll1, IList<IHasGuid> coll2)
+        {
+            if (coll1.Count != coll2.Count)
+                return false;
+            for (int i = 0; i < coll1.Count; i++)
+            {
+                if (coll1[i].GuidValue != coll2[i].GuidValue)
+                    return false;
+            }
+            return true;
+        }
+
+        private static (IList<IHasGuid>, IList<IHasGuid>) LongestCommonSubsequence(IList<IHasGuid> coll1, IList<IHasGuid> coll2)
+        {
+            if (AreCollectionsTheSame(coll1, coll2))
+                return (coll1, coll2);
+            if (coll1.Count == 0 || coll2.Count == 0)
+                return (new List<IHasGuid>(), new List<IHasGuid>());
+
+            //calculate LCS matrix
+            int w = coll1.Count;
+            int h = coll2.Count;
+            int[,] matrix = new int[w, h];
+            for (int j = 0; j < h; j++)
+            {
+                for (int i = 0; i < w; i++)
+                {
+                    if (coll1[i].GuidValue == coll2[j].GuidValue)
+                        matrix[i, j] = (i == 0 || j == 0) ? 1 : matrix[i - 1, j - 1] + 1;
+                    else
+                        matrix[i, j] = (i == 0 || j == 0) ? 0 : Math.Max(matrix[i - 1, j], matrix[i, j - 1]);
+                }
+            }
+
+            //find the actual subsequence
+            List<IHasGuid> subsequence1 = new();
+            List<IHasGuid> subsequence2 = new();
+            int x = w - 1;
+            int y = h - 1;
+            while (x >= 0 || y >= 0)
+            {
+                if (coll1[x].GuidValue == coll2[y].GuidValue)
+                {
+                    subsequence1.Add(coll1[x]);
+                    subsequence2.Add(coll2[y]);
+                    if (x == 0 & y == 0)
+                        break;
+                    if (x > 0)
+                        x--;
+                    if (y > 0)
+                        y--;
+                    continue;
+                }
+                if (x == 0 && y == 0)
+                    break;
+                if (x == 0)
+                {
+                    y--;
+                    continue;
+                }
+                if (y == 0)
+                {
+                    x--;
+                    continue;
+                }
+
+                if (matrix[x - 1, y] > matrix[x, y - 1])
+                    x--;
+                else
+                    y--;
+            }
+
+            subsequence1.Reverse();
+            subsequence2.Reverse();
+            return (subsequence1, subsequence2);
+        }
+
         private static void ViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         private static void ViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
         {
             if (e.NewValue is LayersViewModel vm)
             if (e.NewValue is LayersViewModel vm)
@@ -209,7 +381,7 @@ namespace PixiEditor.Views.UserControls.Layers
 
 
             layer.OpacityUndoTriggerable = val;
             layer.OpacityUndoTriggerable = val;
 
 
-            doc.LayerStructure.ExpandParentGroups(layer.LayerGuid);
+            doc.LayerStructure.ExpandParentGroups(layer.GuidValue);
 
 
             doc.RaisePropertyChange(nameof(doc.LayerStructure));
             doc.RaisePropertyChange(nameof(doc.LayerStructure));
 
 
@@ -259,9 +431,9 @@ namespace PixiEditor.Views.UserControls.Layers
             if (doc.Layers.Count == 0) return;
             if (doc.Layers.Count == 0) return;
 
 
             var layerContainer = (LayerStructureItemContainer)data.GetData(LayerGroupControl.LayerContainerDataName);
             var layerContainer = (LayerStructureItemContainer)data.GetData(LayerGroupControl.LayerContainerDataName);
-            var refLayer = doc.Layers[0].LayerGuid;
-            doc.MoveLayerInStructure(layerContainer.Layer.LayerGuid, refLayer);
-            doc.LayerStructure.AssignParent(layerContainer.Layer.LayerGuid, null);
+            var refLayer = doc.Layers[0].GuidValue;
+            doc.MoveLayerInStructure(layerContainer.Layer.GuidValue, refLayer);
+            doc.LayerStructure.AssignParent(layerContainer.Layer.GuidValue, null);
         }
         }
 
 
         private void HandleGroupControlDrop(IDataObject data)
         private void HandleGroupControlDrop(IDataObject data)

+ 1 - 1
PixiEditor/Views/UserControls/Layers/RawLayersViewer.xaml

@@ -17,7 +17,7 @@
                 <DataTemplate>
                 <DataTemplate>
                     <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
                     <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
                         <Label Foreground="White" Content="{Binding Name}"/>
                         <Label Foreground="White" Content="{Binding Name}"/>
-                        <TextBlock Foreground="Wheat" Text="{Binding LayerGuid}"/>
+                        <TextBlock Foreground="Wheat" Text="{Binding GuidValue}"/>
                     </StackPanel>
                     </StackPanel>
                 </DataTemplate>
                 </DataTemplate>
             </ItemsControl.ItemTemplate>
             </ItemsControl.ItemTemplate>

+ 2 - 2
PixiEditorTests/ModelsTests/DataHoldersTests/DocumentLayersTests.cs

@@ -146,9 +146,9 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
                 layer.IsActive = true;
                 layer.IsActive = true;
             }
             }
 
 
-            document.SetNextSelectedLayerAsActive(document.Layers[1].LayerGuid);
+            document.SetNextSelectedLayerAsActive(document.Layers[1].GuidValue);
 
 
-            Assert.Equal(document.Layers[0].LayerGuid, document.ActiveLayerGuid);
+            Assert.Equal(document.Layers[0].GuidValue, document.ActiveLayerGuid);
         }
         }
     }
     }
 }
 }

+ 41 - 41
PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs

@@ -13,11 +13,11 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             Document doc = new Document(1, 1);
             Document doc = new Document(1, 1);
             doc.Layers.Add(new("_testLayer"));
             doc.Layers.Add(new("_testLayer"));
             var testLayer = doc.Layers[^1];
             var testLayer = doc.Layers[^1];
-            doc.LayerStructure.AddNewGroup("test", testLayer.LayerGuid);
+            doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
 
 
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups);
-            Assert.Equal(testLayer.LayerGuid, doc.LayerStructure.Groups[0].StartLayerGuid);
-            Assert.Equal(testLayer.LayerGuid, doc.LayerStructure.Groups[0].EndLayerGuid);
+            Assert.Equal(testLayer.GuidValue, doc.LayerStructure.Groups[0].StartLayerGuid);
+            Assert.Equal(testLayer.GuidValue, doc.LayerStructure.Groups[0].EndLayerGuid);
         }
         }
 
 
         [Fact]
         [Fact]
@@ -26,15 +26,15 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             Document doc = new Document(1, 1);
             Document doc = new Document(1, 1);
             doc.Layers.Add(new("_testLayer"));
             doc.Layers.Add(new("_testLayer"));
             var testLayer = doc.Layers[^1];
             var testLayer = doc.Layers[^1];
-            doc.LayerStructure.AddNewGroup("test", testLayer.LayerGuid);
-            doc.LayerStructure.AddNewGroup("test1", testLayer.LayerGuid);
+            doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
+            doc.LayerStructure.AddNewGroup("test1", testLayer.GuidValue);
 
 
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups[0].Subgroups);
             Assert.Single(doc.LayerStructure.Groups[0].Subgroups);
-            Assert.Equal(testLayer.LayerGuid, doc.LayerStructure.Groups[0].StartLayerGuid);
-            Assert.Equal(testLayer.LayerGuid, doc.LayerStructure.Groups[0].EndLayerGuid);
-            Assert.Equal(testLayer.LayerGuid, doc.LayerStructure.Groups[0].Subgroups[0].StartLayerGuid);
-            Assert.Equal(testLayer.LayerGuid, doc.LayerStructure.Groups[0].Subgroups[0].EndLayerGuid);
+            Assert.Equal(testLayer.GuidValue, doc.LayerStructure.Groups[0].StartLayerGuid);
+            Assert.Equal(testLayer.GuidValue, doc.LayerStructure.Groups[0].EndLayerGuid);
+            Assert.Equal(testLayer.GuidValue, doc.LayerStructure.Groups[0].Subgroups[0].StartLayerGuid);
+            Assert.Equal(testLayer.GuidValue, doc.LayerStructure.Groups[0].Subgroups[0].EndLayerGuid);
         }
         }
 
 
         [Fact]
         [Fact]
@@ -45,8 +45,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             doc.Layers.Add(new Layer("_testLayer1"));
             doc.Layers.Add(new Layer("_testLayer1"));
             var testLayer = doc.Layers[0];
             var testLayer = doc.Layers[0];
             var testLayer1 = doc.Layers[^1];
             var testLayer1 = doc.Layers[^1];
-            doc.LayerStructure.AddNewGroup("test", testLayer.LayerGuid);
-            doc.LayerStructure.AddNewGroup("test1", testLayer1.LayerGuid);
+            doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
+            doc.LayerStructure.AddNewGroup("test1", testLayer1.GuidValue);
 
 
             Assert.Equal(0, doc.Layers.IndexOf(testLayer));
             Assert.Equal(0, doc.Layers.IndexOf(testLayer));
             Assert.Equal(1, doc.Layers.IndexOf(testLayer1));
             Assert.Equal(1, doc.Layers.IndexOf(testLayer1));
@@ -62,8 +62,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             LayerStructure ls = new LayerStructure(new Document(1, 1));
             LayerStructure ls = new LayerStructure(new Document(1, 1));
             Layer testLayer = new Layer("tst");
             Layer testLayer = new Layer("tst");
-            ls.Groups.Add(new GuidStructureItem("group 1", testLayer.LayerGuid));
-            ls.Groups[0].Subgroups.Add(new GuidStructureItem("group 1 nested", testLayer.LayerGuid));
+            ls.Groups.Add(new GuidStructureItem("group 1", testLayer.GuidValue));
+            ls.Groups[0].Subgroups.Add(new GuidStructureItem("group 1 nested", testLayer.GuidValue));
 
 
             Assert.True(ls.IsChildOf(ls.Groups[0].Subgroups[0], ls.Groups[0]));
             Assert.True(ls.IsChildOf(ls.Groups[0].Subgroups[0], ls.Groups[0]));
             Assert.False(ls.IsChildOf(ls.Groups[0], ls.Groups[0].Subgroups[0]));
             Assert.False(ls.IsChildOf(ls.Groups[0], ls.Groups[0].Subgroups[0]));
@@ -74,7 +74,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             var doc = new Document(1, 1);
             var doc = new Document(1, 1);
             doc.Layers.Add(new Layer("tst"));
             doc.Layers.Add(new Layer("tst"));
-            Guid testLayerGuid = doc.Layers[0].LayerGuid;
+            Guid testLayerGuid = doc.Layers[0].GuidValue;
             LayerStructure ls = new LayerStructure(doc);
             LayerStructure ls = new LayerStructure(doc);
             ls.AddNewGroup("Test group", testLayerGuid);
             ls.AddNewGroup("Test group", testLayerGuid);
             ls.AddNewGroup("Test group nested", testLayerGuid);
             ls.AddNewGroup("Test group nested", testLayerGuid);
@@ -88,7 +88,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             var doc = new Document(1, 1);
             var doc = new Document(1, 1);
             doc.Layers.Add(new Layer("layer"));
             doc.Layers.Add(new Layer("layer"));
-            var guid = doc.Layers[0].LayerGuid;
+            var guid = doc.Layers[0].GuidValue;
             doc.LayerStructure.AddNewGroup("layer group", guid);
             doc.LayerStructure.AddNewGroup("layer group", guid);
             Assert.True(LayerStructure.GroupContainsOnlyLayer(guid, doc.LayerStructure.Groups[0]));
             Assert.True(LayerStructure.GroupContainsOnlyLayer(guid, doc.LayerStructure.Groups[0]));
         }
         }
@@ -98,7 +98,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             var doc = new Document(1, 1);
             var doc = new Document(1, 1);
             doc.Layers.Add(new Layer("layer"));
             doc.Layers.Add(new Layer("layer"));
-            var guid = doc.Layers[0].LayerGuid;
+            var guid = doc.Layers[0].GuidValue;
             doc.LayerStructure.AddNewGroup("layer group", guid);
             doc.LayerStructure.AddNewGroup("layer group", guid);
             doc.LayerStructure.AddNewGroup("layer group nested", guid);
             doc.LayerStructure.AddNewGroup("layer group nested", guid);
             Assert.False(LayerStructure.GroupContainsOnlyLayer(guid, doc.LayerStructure.Groups[0]));
             Assert.False(LayerStructure.GroupContainsOnlyLayer(guid, doc.LayerStructure.Groups[0]));
@@ -112,7 +112,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             doc.Layers.Add(new("Test"));
             doc.Layers.Add(new("Test"));
             doc.Layers.Add(new("Test2"));
             doc.Layers.Add(new("Test2"));
             LayerStructure structure = new(doc);
             LayerStructure structure = new(doc);
-            structure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
+            structure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
             var clone = structure.CloneGroups();
             var clone = structure.CloneGroups();
 
 
@@ -136,7 +136,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
             doc.Layers.Add(new("Test"));
             doc.Layers.Add(new("Test"));
-            var group = doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
+            var group = doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
             Assert.Equal(group.GroupGuid, doc.LayerStructure.GetGroupByGuid(group.GroupGuid).GroupGuid);
             Assert.Equal(group.GroupGuid, doc.LayerStructure.GetGroupByGuid(group.GroupGuid).GroupGuid);
         }
         }
@@ -146,10 +146,10 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
             doc.Layers.Add(new("Test"));
             doc.Layers.Add(new("Test"));
-            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
-            var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].LayerGuid);
+            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
+            var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
 
 
-            doc.LayerStructure.PreMoveReassignBounds(new GroupData(group1.GroupGuid), doc.Layers[0].LayerGuid);
+            doc.LayerStructure.PreMoveReassignBounds(new GroupData(group1.GroupGuid), doc.Layers[0].GuidValue);
 
 
             Assert.Empty(doc.LayerStructure.Groups);
             Assert.Empty(doc.LayerStructure.Groups);
         }
         }
@@ -159,22 +159,22 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         {
         {
             Document doc = new(1, 1);
             Document doc = new(1, 1);
             doc.Layers.Add(new("Test"));
             doc.Layers.Add(new("Test"));
-            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
-            var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].LayerGuid);
+            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
+            var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
 
 
             doc.Layers.Add(new("Test 1"));
             doc.Layers.Add(new("Test 1"));
 
 
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
 
 
-            doc.LayerStructure.PostMoveReassignBounds(new GroupData(group1.GroupGuid), layer.LayerGuid);
+            doc.LayerStructure.PostMoveReassignBounds(new GroupData(group1.GroupGuid), layer.GuidValue);
 
 
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups);
             Assert.Single(doc.LayerStructure.Groups[0].Subgroups);
             Assert.Single(doc.LayerStructure.Groups[0].Subgroups);
-            Assert.Equal(layer.LayerGuid, doc.LayerStructure.Groups[0].Subgroups[0].EndLayerGuid);
-            Assert.Equal(firstLayer.LayerGuid, doc.LayerStructure.Groups[0].Subgroups[0].StartLayerGuid);
-            Assert.Equal(layer.LayerGuid, doc.LayerStructure.Groups[0].EndLayerGuid);
-            Assert.Equal(firstLayer.LayerGuid, doc.LayerStructure.Groups[0].StartLayerGuid);
+            Assert.Equal(layer.GuidValue, doc.LayerStructure.Groups[0].Subgroups[0].EndLayerGuid);
+            Assert.Equal(firstLayer.GuidValue, doc.LayerStructure.Groups[0].Subgroups[0].StartLayerGuid);
+            Assert.Equal(layer.GuidValue, doc.LayerStructure.Groups[0].EndLayerGuid);
+            Assert.Equal(firstLayer.GuidValue, doc.LayerStructure.Groups[0].StartLayerGuid);
         }
         }
 
 
         [Fact]
         [Fact]
@@ -185,16 +185,16 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
 
 
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
 
 
-            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
+            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
             doc.Layers.Add(new Layer("Test 1"));
             doc.Layers.Add(new Layer("Test 1"));
 
 
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
 
 
-            doc.LayerStructure.AssignParent(doc.Layers[^1].LayerGuid, doc.LayerStructure.Groups[0].GroupGuid);
+            doc.LayerStructure.AssignParent(doc.Layers[^1].GuidValue, doc.LayerStructure.Groups[0].GroupGuid);
 
 
-            Assert.Equal(layer.LayerGuid, doc.LayerStructure.Groups[0].EndLayerGuid);
-            Assert.Equal(firstLayer.LayerGuid, doc.LayerStructure.Groups[0].StartLayerGuid);
+            Assert.Equal(layer.GuidValue, doc.LayerStructure.Groups[0].EndLayerGuid);
+            Assert.Equal(firstLayer.GuidValue, doc.LayerStructure.Groups[0].StartLayerGuid);
         }
         }
 
 
         [Fact]
         [Fact]
@@ -205,17 +205,17 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
 
 
             var firstLayer = doc.Layers[0];
             var firstLayer = doc.Layers[0];
 
 
-            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
+            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
             doc.Layers.Add(new Layer("Test 1"));
             doc.Layers.Add(new Layer("Test 1"));
 
 
             var layer = doc.Layers[^1];
             var layer = doc.Layers[^1];
 
 
-            doc.LayerStructure.AssignParent(layer.LayerGuid, doc.LayerStructure.Groups[0].GroupGuid);
-            doc.LayerStructure.AssignParent(layer.LayerGuid, null);
+            doc.LayerStructure.AssignParent(layer.GuidValue, doc.LayerStructure.Groups[0].GroupGuid);
+            doc.LayerStructure.AssignParent(layer.GuidValue, null);
 
 
-            Assert.Equal(firstLayer.LayerGuid, doc.LayerStructure.Groups[0].EndLayerGuid);
-            Assert.Equal(firstLayer.LayerGuid, doc.LayerStructure.Groups[0].StartLayerGuid);
+            Assert.Equal(firstLayer.GuidValue, doc.LayerStructure.Groups[0].EndLayerGuid);
+            Assert.Equal(firstLayer.GuidValue, doc.LayerStructure.Groups[0].StartLayerGuid);
         }
         }
 
 
         [Fact]
         [Fact]
@@ -226,11 +226,11 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             doc.Layers.Add(new Layer("Test 1"));
             doc.Layers.Add(new Layer("Test 1"));
             doc.Layers.Add(new Layer("Test 2"));
             doc.Layers.Add(new Layer("Test 2"));
             doc.Layers.Add(new Layer("Test 3"));
             doc.Layers.Add(new Layer("Test 3"));
-            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].LayerGuid);
+            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
 
-            doc.LayerStructure.AssignParent(doc.Layers[1].LayerGuid, doc.LayerStructure.Groups[0].GroupGuid);
-            doc.LayerStructure.AssignParent(doc.Layers[2].LayerGuid, doc.LayerStructure.Groups[0].GroupGuid);
-            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[2].LayerGuid);
+            doc.LayerStructure.AssignParent(doc.Layers[1].GuidValue, doc.LayerStructure.Groups[0].GroupGuid);
+            doc.LayerStructure.AssignParent(doc.Layers[2].GuidValue, doc.LayerStructure.Groups[0].GroupGuid);
+            doc.LayerStructure.AddNewGroup("Test group", doc.Layers[2].GuidValue);
 
 
             var layersInGroup = doc.LayerStructure.GetGroupLayers(doc.LayerStructure.Groups[0]);
             var layersInGroup = doc.LayerStructure.GetGroupLayers(doc.LayerStructure.Groups[0]);