Browse Source

Added undo remove layer

flabbet 4 years ago
parent
commit
5c7a2fd2a7

+ 49 - 3
PixiEditor/Models/DataHolders/Document.cs

@@ -317,6 +317,21 @@ namespace PixiEditor.Models.DataHolders
             LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
         }
 
+        public void SetNextLayerAsActive(int lastLayerIndex)
+        {
+            if (Layers.Count > 0)
+            {
+                if (lastLayerIndex == 0)
+                {
+                    SetActiveLayer(0);
+                }
+                else
+                {
+                    SetActiveLayer(lastLayerIndex - 1);
+                }
+            }
+        }
+
         public void RemoveLayer(int layerIndex)
         {
             if (Layers.Count == 0)
@@ -325,14 +340,45 @@ namespace PixiEditor.Models.DataHolders
             }
 
             bool wasActive = Layers[layerIndex].IsActive;
+
+            StorageBasedChange change = new StorageBasedChange(this, new[] { Layers[layerIndex] });
+
+            UndoManager.AddUndoChange(
+                change.ToChange(RestoreLayersProcess, RemoveLayerProcess, new object[] { Layers[layerIndex].LayerGuid }, "Remove layer"));
             Layers.RemoveAt(layerIndex);
             if (wasActive)
             {
-                SetActiveLayer(0);
+                SetNextLayerAsActive(layerIndex);
             }
-            else if (ActiveLayerIndex > Layers.Count - 1)
+        }
+
+        private void RestoreLayersProcess(Layer[] layers, UndoLayer[] layersData)
+        {
+            for (int i = 0; i < layers.Length; i++)
             {
-                SetActiveLayer(Layers.Count - 1);
+                Layer layer = layers[i];
+
+                Layers.Insert(layersData[i].LayerIndex, layer);
+                if (layer.IsActive)
+                {
+                    SetActiveLayer(Layers.IndexOf(layer));
+                }
+            }
+        }
+
+        private void RemoveLayerProcess(object[] parameters)
+        {
+            if (parameters != null && parameters.Length > 0 && parameters[0] is Guid layerGuid)
+            {
+                Layer layer = Layers.First(x => x.LayerGuid == layerGuid);
+                int index = Layers.IndexOf(layer);
+                bool wasActive = layer.IsActive;
+                Layers.Remove(layer);
+
+                if (wasActive)
+                {
+                    SetNextLayerAsActive(index);
+                }
             }
         }
 

+ 5 - 0
PixiEditor/Models/Layers/Layer.cs

@@ -32,6 +32,7 @@ namespace PixiEditor.Models.Layers
             LayerBitmap = BitmapFactory.New(0, 0);
             Width = 0;
             Height = 0;
+            LayerGuid = Guid.NewGuid();
         }
 
         public Layer(string name, int width, int height)
@@ -40,6 +41,7 @@ namespace PixiEditor.Models.Layers
             LayerBitmap = BitmapFactory.New(width, height);
             Width = width;
             Height = height;
+            LayerGuid = Guid.NewGuid();
         }
 
         public Layer(string name, WriteableBitmap layerBitmap)
@@ -48,10 +50,13 @@ namespace PixiEditor.Models.Layers
             LayerBitmap = layerBitmap;
             Width = layerBitmap.PixelWidth;
             Height = layerBitmap.PixelHeight;
+            LayerGuid = Guid.NewGuid();
         }
 
         public Dictionary<Coordinates, Color> LastRelativeCoordinates { get; set; }
 
+        public Guid LayerGuid { get; init; }
+
         public string Name
         {
             get => name;

+ 39 - 2
PixiEditor/Models/Undo/StorageBasedChange.cs

@@ -13,6 +13,8 @@ namespace PixiEditor.Models.Undo
 {
     public class StorageBasedChange
     {
+        public static string DefaultUndoChangeLocation => Path.Join(Path.GetTempPath(), "PixiEditor", "UndoStack");
+
         public string UndoChangeLocation { get; set; }
 
         public UndoLayer[] StoredLayers { get; set; }
@@ -21,12 +23,29 @@ namespace PixiEditor.Models.Undo
 
         private Document document;
 
-        public StorageBasedChange(Document doc, IEnumerable<Layer> layers, string undoChangeLocation)
+        public StorageBasedChange(Document doc, IEnumerable<Layer> layers, bool saveOnStartup = true)
+        {
+            document = doc;
+            layersToStore = layers;
+            UndoChangeLocation = DefaultUndoChangeLocation;
+            GenerateUndoLayers();
+            if (saveOnStartup)
+            {
+                SaveLayersOnDevice();
+            }
+        }
+
+        public StorageBasedChange(Document doc, IEnumerable<Layer> layers, string undoChangeLocation, bool saveOnStartup = true)
         {
             document = doc;
             layersToStore = layers;
             UndoChangeLocation = undoChangeLocation;
             GenerateUndoLayers();
+
+            if (saveOnStartup)
+            {
+                SaveLayersOnDevice();
+            }
         }
 
         public void SaveLayersOnDevice()
@@ -39,7 +58,7 @@ namespace PixiEditor.Models.Undo
                 i++;
             }
 
-            layersToStore = null;
+            layersToStore = Array.Empty<Layer>();
         }
 
         public Layer[] LoadLayersFromDevice()
@@ -57,6 +76,7 @@ namespace PixiEditor.Models.Undo
                     MaxHeight = storedLayer.MaxHeight,
                     IsVisible = storedLayer.IsVisible,
                     IsActive = storedLayer.IsActive,
+                    LayerGuid = storedLayer.LayerGuid
                 };
 
                 File.Delete(StoredLayers[i].StoredPngLayerName);
@@ -66,6 +86,23 @@ namespace PixiEditor.Models.Undo
             return layers;
         }
 
+        public Change ToChange(Action<Layer[], UndoLayer[]> undoProcess, Action<object[]> redoProcess, object[] redoProcessParameters, string description = "")
+        {
+            Action<object[]> finalUndoProcess = _ =>
+            {
+                Layer[] layers = LoadLayersFromDevice();
+                undoProcess(layers, StoredLayers);
+            };
+
+            Action<object[]> fianlRedoProcess = parameters =>
+            {
+                SaveLayersOnDevice();
+                redoProcess(parameters);
+            };
+
+            return new Change(finalUndoProcess, null, fianlRedoProcess, redoProcessParameters, description);
+        }
+
         private void GenerateUndoLayers()
         {
             StoredLayers = new UndoLayer[layersToStore.Count()];

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

@@ -1,5 +1,5 @@
-using PixiEditor.Models.Layers;
-using System;
+using System;
+using PixiEditor.Models.Layers;
 
 namespace PixiEditor.Models.Undo
 {
@@ -8,6 +8,8 @@ namespace PixiEditor.Models.Undo
     {
         public string StoredPngLayerName { get; set; }
 
+        public Guid LayerGuid { get; init; }
+
         public string Name { get; set; }
 
         public int LayerIndex { get; set; }
@@ -43,6 +45,7 @@ namespace PixiEditor.Models.Undo
             OffsetY = layer.OffsetY;
             Opacity = layer.Opacity;
             IsActive = layer.IsActive;
+            LayerGuid = layer.LayerGuid;
         }
     }
 }

+ 19 - 0
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 using System.Linq;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
@@ -34,6 +35,12 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         {
             UndoCommand = new RelayCommand(Undo, CanUndo);
             RedoCommand = new RelayCommand(Redo, CanRedo);
+            if (!Directory.Exists(StorageBasedChange.DefaultUndoChangeLocation))
+            {
+                Directory.CreateDirectory(StorageBasedChange.DefaultUndoChangeLocation);
+            }
+
+            ClearUndoTempDirectory();
         }
 
         public void TriggerNewUndoChange(Tool toolUsed)
@@ -72,6 +79,18 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             Owner.BitmapManager.ActiveDocument.UndoManager.Undo();
         }
 
+        /// <summary>
+        /// Removes all files from %tmp%/PixiEditor/UndoStack/.
+        /// </summary>
+        public void ClearUndoTempDirectory()
+        {
+            DirectoryInfo dirInfo = new DirectoryInfo(StorageBasedChange.DefaultUndoChangeLocation);
+            foreach (FileInfo file in dirInfo.GetFiles())
+            {
+                file.Delete();
+            }
+        }
+
         /// <summary>
         ///     Returns true if undo can be done.
         /// </summary>

+ 64 - 3
PixiEditorTests/ModelsTests/UndoTests/StorageBasedChangeTests.cs

@@ -7,6 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Undo;
@@ -76,7 +77,6 @@ namespace PixiEditorTests.ModelsTests.UndoTests
             Document document = GenerateTestDocument();
 
             StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
-            change.SaveLayersOnDevice();
 
             foreach (var layer in change.StoredLayers)
             {
@@ -92,8 +92,6 @@ namespace PixiEditorTests.ModelsTests.UndoTests
 
             StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
 
-            change.SaveLayersOnDevice();
-
             Layer[] layers = change.LoadLayersFromDevice();
 
             Assert.Equal(document.Layers.Count, layers.Length);
@@ -104,5 +102,68 @@ namespace PixiEditorTests.ModelsTests.UndoTests
                 LayersTestHelper.LayersAreEqual(expected, actual);
             }
         }
+
+        [Fact]
+        public void TestThatUndoInvokesLoadFromDeviceAndExecutesProcess()
+        {
+            Document document = GenerateTestDocument();
+
+            StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
+            bool undoInvoked = false;
+
+            Action<Layer[], UndoLayer[]> testUndoProcess = (layers, data) =>
+            {
+                undoInvoked = true;
+                Assert.Equal(document.Layers.Count, layers.Length);
+                Assert.Equal(document.Layers.Count, data.Length);
+                foreach (var undoLayer in data)
+                {
+                    Assert.False(File.Exists(undoLayer.StoredPngLayerName));
+                }
+            };
+
+            Action<object[]> testRedoProcess = parameters => { };
+
+            Change undoChange = change.ToChange(testUndoProcess, testRedoProcess, null);
+            UndoManager manager = new UndoManager(this);
+
+            manager.AddUndoChange(undoChange);
+            manager.Undo();
+
+            Assert.True(undoInvoked);
+        }
+
+        [Fact]
+        public void TestThatRedoInvokesSaveToDeviceAndExecutesProcess()
+        {
+            Document document = GenerateTestDocument();
+
+            StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
+            bool redoInvoked = false;
+
+            Action<Layer[], UndoLayer[]> testUndoProcess = (layers, data) => { };
+
+            Action<object[]> testRedoProcess = parameters =>
+            {
+                redoInvoked = true;
+                foreach (var undoLayer in change.StoredLayers)
+                {
+                    Assert.True(File.Exists(undoLayer.StoredPngLayerName));
+                    Assert.NotNull(parameters);
+                    Assert.Single(parameters);
+                    Assert.IsType<int>(parameters[0]);
+                    Assert.Equal(2, parameters[0]);
+                }
+            };
+
+            Change undoChange = change.ToChange(testUndoProcess, testRedoProcess, new object[] { 2 });
+            UndoManager manager = new UndoManager(this);
+
+            manager.AddUndoChange(undoChange);
+            manager.Undo();
+            manager.Redo();
+
+            Assert.True(redoInvoked);
+        }
     }
 }