Browse Source

Merge pull request #155 from PixiEditor/multiple-layers-selection

Multiple layers selection
Krzysztof Krysiński 4 years ago
parent
commit
5894bb517b
28 changed files with 1212 additions and 747 deletions
  1. 0 27
      PixiEditor/Helpers/Converters/BoolToColorConverter.cs
  2. 15 3
      PixiEditor/Models/Controllers/BitmapManager.cs
  3. 44 12
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  4. 3 3
      PixiEditor/Models/Controllers/LayersChangedEventArgs.cs
  5. 229 66
      PixiEditor/Models/DataHolders/Document/Document.Layers.cs
  6. 32 19
      PixiEditor/Models/DataHolders/Document/Document.cs
  7. 1 1
      PixiEditor/Models/DataHolders/Selection.cs
  8. 4 1
      PixiEditor/Models/Events/DocumentChangedEventArgs.cs
  9. 1 1
      PixiEditor/Models/ImageManipulation/BitmapUtils.cs
  10. 12 1
      PixiEditor/Models/Layers/Layer.cs
  11. 5 0
      PixiEditor/Models/Tools/Tool.cs
  12. 69 60
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  13. 89 89
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  14. 1 0
      PixiEditor/Models/Undo/StorageBasedChange.cs
  15. 3 0
      PixiEditor/Models/Undo/UndoLayer.cs
  16. 1 1
      PixiEditor/Styles/ComboBoxDarkStyle.xaml
  17. 1 1
      PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs
  18. 1 1
      PixiEditor/ViewModels/SubViewModels/Main/DocumentViewModel.cs
  19. 87 16
      PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs
  20. 1 1
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  21. 2 3
      PixiEditor/ViewModels/ViewModelMain.cs
  22. 438 435
      PixiEditor/Views/MainWindow.xaml
  23. 1 0
      PixiEditor/Views/MainWindow.xaml.cs
  24. 1 4
      PixiEditor/Views/UserControls/LayerItem.xaml
  25. 10 0
      PixiEditor/Views/UserControls/LayerItem.xaml.cs
  26. 154 0
      PixiEditorTests/ModelsTests/DataHoldersTests/DocumentLayersTests.cs
  27. 6 1
      PixiEditorTests/ModelsTests/DataHoldersTests/DocumentTests.cs
  28. 1 1
      PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs

+ 0 - 27
PixiEditor/Helpers/Converters/BoolToColorConverter.cs

@@ -1,27 +0,0 @@
-using System;
-using System.Globalization;
-using System.Windows.Data;
-
-namespace PixiEditor.Helpers.Converters
-{
-    public class BoolToColorConverter : IValueConverter
-    {
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return value?.ToString() == "Transparent";
-        }
-
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (value is bool boolean)
-            {
-                if (boolean == false)
-                {
-                    return "Transparent";
-                }
-            }
-
-            return "#505056";
-        }
-    }
-}

+ 15 - 3
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -24,6 +24,7 @@ namespace PixiEditor.Models.Controllers
     {
     {
         private Document activeDocument;
         private Document activeDocument;
         private Tool selectedTool;
         private Tool selectedTool;
+        private Coordinates? startPosition = null;
 
 
         public BitmapManager()
         public BitmapManager()
         {
         {
@@ -72,20 +73,23 @@ namespace PixiEditor.Models.Controllers
 
 
         public BitmapOperationsUtility BitmapOperations { get; set; }
         public BitmapOperationsUtility BitmapOperations { get; set; }
 
 
-        public ReadonlyToolUtility ReadonlyToolUtility { get; set; }
-
+        public ReadonlyToolUtility ReadonlyToolUtility { get; set; }
+
+#nullable enable
         public Document ActiveDocument
         public Document ActiveDocument
         {
         {
             get => activeDocument;
             get => activeDocument;
             set
             set
             {
             {
                 activeDocument?.UpdatePreviewImage();
                 activeDocument?.UpdatePreviewImage();
+                Document? oldDoc = activeDocument;
                 activeDocument = value;
                 activeDocument = value;
                 RaisePropertyChanged(nameof(ActiveDocument));
                 RaisePropertyChanged(nameof(ActiveDocument));
-                DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value));
+                DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
             }
             }
         }
         }
 
 
+#nullable disable
         public ObservableCollection<Document> Documents { get; set; } = new ObservableCollection<Document>();
         public ObservableCollection<Document> Documents { get; set; } = new ObservableCollection<Document>();
 
 
         /// <summary>
         /// <summary>
@@ -113,6 +117,12 @@ namespace PixiEditor.Models.Controllers
         {
         {
             if (SelectedTool.CanStartOutsideCanvas || clickedOnCanvas)
             if (SelectedTool.CanStartOutsideCanvas || clickedOnCanvas)
             {
             {
+                if (startPosition == null)
+                {
+                    SelectedTool.OnStart(newPosition);
+                    startPosition = newPosition;
+                }
+
                 if (IsOperationTool(SelectedTool))
                 if (IsOperationTool(SelectedTool))
                 {
                 {
                     BitmapOperations.ExecuteTool(newPosition, MouseController.LastMouseMoveCoordinates.ToList(), (BitmapOperationTool)SelectedTool);
                     BitmapOperations.ExecuteTool(newPosition, MouseController.LastMouseMoveCoordinates.ToList(), (BitmapOperationTool)SelectedTool);
@@ -193,6 +203,8 @@ namespace PixiEditor.Models.Controllers
             {
             {
                 BitmapOperations.ApplyPreviewLayer();
                 BitmapOperations.ApplyPreviewLayer();
             }
             }
+
+            startPosition = null;
         }
         }
 
 
         private void HighlightPixels(Coordinates newPosition)
         private void HighlightPixels(Coordinates newPosition)

+ 44 - 12
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -12,6 +12,7 @@ using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
+using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.Controllers
 namespace PixiEditor.Models.Controllers
 {
 {
@@ -87,20 +88,29 @@ namespace PixiEditor.Models.Controllers
                 return;
                 return;
             }
             }
 
 
-            foreach (var modifiedLayer in previewLayerChanges)
+            Layer[] layers = new Layer[previewLayerChanges.Count];
+
+            for (int i = 0; i < layers.Length; i++)
+            {
+                layers[i] = Manager.ActiveDocument.Layers.First(x => x.LayerGuid == previewLayerChanges[i].LayerGuid);
+            }
+
+            if (layers.Length > 0)
             {
             {
-                Layer layer = Manager.ActiveDocument.Layers.FirstOrDefault(x => x.LayerGuid == modifiedLayer.LayerGuid);
+                IEnumerable<LayerChange> oldValues =
+                    ApplyToLayers(layers, previewLayerChanges.ToArray());
 
 
-                if (layer != null)
+                foreach (var oldValue in oldValues)
                 {
                 {
-                    BitmapPixelChanges oldValues = ApplyToLayer(layer, modifiedLayer).PixelChanges;
+                    var previewChanges = previewLayerChanges.First(x => x.LayerGuid == oldValue.LayerGuid);
 
 
                     BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
                     BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
-                        modifiedLayer.PixelChanges,
-                        oldValues,
-                        modifiedLayer.LayerGuid));
-                    Manager.ActiveDocument.GeneratePreviewLayer();
+                        previewChanges.PixelChanges,
+                        oldValue.PixelChanges,
+                        previewChanges.LayerGuid));
                 }
                 }
+
+                Manager.ActiveDocument.GeneratePreviewLayer();
             }
             }
 
 
             previewLayerChanges = null;
             previewLayerChanges = null;
@@ -136,14 +146,31 @@ namespace PixiEditor.Models.Controllers
 
 
         private LayerChange ApplyToLayer(Layer layer, LayerChange change)
         private LayerChange ApplyToLayer(Layer layer, LayerChange change)
         {
         {
-            layer.DynamicResize(change.PixelChanges);
+            return ApplyToLayers(new Layer[] { layer }, new LayerChange[] { change })[0];
+        }
 
 
-            LayerChange oldPixelsValues = new LayerChange(
+        private LayerChange[] ApplyToLayers(Layer[] layers, LayerChange[] changes)
+        {
+            LayerChange[] oldPixelValues = new LayerChange[changes.Length];
+            for (int i = 0; i < layers.Length; i++)
+            {
+                Layer layer = layers[i];
+                LayerChange change = changes.First(x => x.LayerGuid == layer.LayerGuid);
+                layer.DynamicResize(change.PixelChanges);
+
+                oldPixelValues[i] = new LayerChange(
                 GetOldPixelsValues(change.PixelChanges.ChangedPixels.Keys.ToArray()),
                 GetOldPixelsValues(change.PixelChanges.ChangedPixels.Keys.ToArray()),
                 change.LayerGuid);
                 change.LayerGuid);
+            }
+
+            for (int i = 0; i < layers.Length; i++)
+            {
+                Layer layer = layers[i];
+                LayerChange change = changes.First(x => x.LayerGuid == layer.LayerGuid);
+                layer.SetPixels(change.PixelChanges, false);
+            }
 
 
-            layer.SetPixels(change.PixelChanges, false);
-            return oldPixelsValues;
+            return oldPixelValues;
         }
         }
 
 
         private bool MouseCordsNotInLine(List<Coordinates> cords)
         private bool MouseCordsNotInLine(List<Coordinates> cords)
@@ -205,6 +232,11 @@ namespace PixiEditor.Models.Controllers
                     Manager.PrimaryColor);
                     Manager.PrimaryColor);
 
 
                 BitmapPixelChanges[] changes = modifiedLayers.Select(x => x.PixelChanges).ToArray();
                 BitmapPixelChanges[] changes = modifiedLayers.Select(x => x.PixelChanges).ToArray();
+                if (changes.Length == 0)
+                {
+                    return;
+                }
+
                 Manager.ActiveDocument.PreviewLayer.SetPixels(BitmapPixelChanges.CombineOverride(changes));
                 Manager.ActiveDocument.PreviewLayer.SetPixels(BitmapPixelChanges.CombineOverride(changes));
 
 
                 if (clearPreviewLayer || previewLayerChanges == null)
                 if (clearPreviewLayer || previewLayerChanges == null)

+ 3 - 3
PixiEditor/Models/Controllers/LayersChangedEventArgs.cs

@@ -5,13 +5,13 @@ namespace PixiEditor.Models.Controllers
 {
 {
     public class LayersChangedEventArgs : EventArgs
     public class LayersChangedEventArgs : EventArgs
     {
     {
-        public LayersChangedEventArgs(int layerAffected, LayerAction layerChangeType)
+        public LayersChangedEventArgs(Guid layerAffectedGuid, LayerAction layerChangeType)
         {
         {
-            LayerAffected = layerAffected;
+            LayerAffectedGuid = layerAffectedGuid;
             LayerChangeType = layerChangeType;
             LayerChangeType = layerChangeType;
         }
         }
 
 
-        public int LayerAffected { get; set; }
+        public Guid LayerAffectedGuid { get; set; }
 
 
         public LayerAction LayerChangeType { get; set; }
         public LayerAction LayerChangeType { get; set; }
     }
     }

+ 229 - 66
PixiEditor/Models/DataHolders/Document/Document.Layers.cs

@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
+using GalaSoft.MvvmLight.Messaging;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
@@ -14,41 +15,60 @@ namespace PixiEditor.Models.DataHolders
 {
 {
     public partial class Document
     public partial class Document
     {
     {
-        private int activeLayerIndex;
+        public const string MainSelectedLayerColor = "#505056";
+        public const string SecondarySelectedLayerColor = "#7D505056";
+        private Guid activeLayerGuid;
 
 
         public ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
         public ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
 
 
-        public Layer ActiveLayer => Layers.Count > 0 ? Layers[ActiveLayerIndex] : null;
+        public Layer ActiveLayer => Layers.Count > 0 ? Layers.FirstOrDefault(x => x.LayerGuid == ActiveLayerGuid) : null;
 
 
-        public int ActiveLayerIndex
+        public Guid ActiveLayerGuid
         {
         {
-            get => activeLayerIndex;
+            get => activeLayerGuid;
             set
             set
             {
             {
-                activeLayerIndex = value;
-                RaisePropertyChanged(nameof(ActiveLayerIndex));
+                activeLayerGuid = value;
+                RaisePropertyChanged(nameof(ActiveLayerGuid));
                 RaisePropertyChanged(nameof(ActiveLayer));
                 RaisePropertyChanged(nameof(ActiveLayer));
             }
             }
         }
         }
 
 
         public event EventHandler<LayersChangedEventArgs> LayersChanged;
         public event EventHandler<LayersChangedEventArgs> LayersChanged;
 
 
-        public void SetActiveLayer(int index)
+        public void SetMainActiveLayer(int index)
         {
         {
-            if (ActiveLayerIndex <= Layers.Count - 1)
+            if (ActiveLayer != null && Layers.IndexOf(ActiveLayer) <= Layers.Count - 1)
             {
             {
                 ActiveLayer.IsActive = false;
                 ActiveLayer.IsActive = false;
             }
             }
 
 
-            if (Layers.Any(x => x.IsActive))
+            foreach (var layer in Layers)
             {
             {
-                var guids = Layers.Where(x => x.IsActive).Select(y => y.LayerGuid);
-                guids.ToList().ForEach(x => Layers.First(layer => layer.LayerGuid == x).IsActive = false);
+                if (layer.IsActive)
+                {
+                    layer.IsActive = false;
+                }
             }
             }
 
 
-            ActiveLayerIndex = index;
+            ActiveLayerGuid = Layers[index].LayerGuid;
             ActiveLayer.IsActive = true;
             ActiveLayer.IsActive = true;
-            LayersChanged?.Invoke(this, new LayersChangedEventArgs(index, LayerAction.SetActive));
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(ActiveLayerGuid, LayerAction.SetActive));
+        }
+
+        public void UpdateLayersColor()
+        {
+            foreach (var layer in Layers)
+            {
+                if (layer.LayerGuid == ActiveLayerGuid)
+                {
+                    layer.LayerHighlightColor = MainSelectedLayerColor;
+                }
+                else
+                {
+                    layer.LayerHighlightColor = SecondarySelectedLayerColor;
+                }
+            }
         }
         }
 
 
         public void MoveLayerIndexBy(int layerIndex, int amount)
         public void MoveLayerIndexBy(int layerIndex, int amount)
@@ -83,7 +103,7 @@ namespace PixiEditor.Models.DataHolders
             });
             });
             if (setAsActive)
             if (setAsActive)
             {
             {
-                SetActiveLayer(Layers.Count - 1);
+                SetMainActiveLayer(Layers.Count - 1);
             }
             }
 
 
             if (Layers.Count > 1)
             if (Layers.Count > 1)
@@ -97,7 +117,7 @@ namespace PixiEditor.Models.DataHolders
                         "Add layer"));
                         "Add layer"));
             }
             }
 
 
-            LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(Layers[0].LayerGuid, LayerAction.Add));
         }
         }
 
 
         public void SetNextLayerAsActive(int lastLayerIndex)
         public void SetNextLayerAsActive(int lastLayerIndex)
@@ -106,12 +126,74 @@ namespace PixiEditor.Models.DataHolders
             {
             {
                 if (lastLayerIndex == 0)
                 if (lastLayerIndex == 0)
                 {
                 {
-                    SetActiveLayer(0);
+                    SetMainActiveLayer(0);
                 }
                 }
                 else
                 else
                 {
                 {
-                    SetActiveLayer(lastLayerIndex - 1);
+                    SetMainActiveLayer(lastLayerIndex - 1);
+                }
+            }
+        }
+
+        public void SetNextSelectedLayerAsActive(Guid lastLayerGuid)
+        {
+            var selectedLayers = Layers.Where(x => x.IsActive);
+            foreach (var layer in selectedLayers)
+            {
+                if (layer.LayerGuid != lastLayerGuid)
+                {
+                    ActiveLayerGuid = layer.LayerGuid;
+                    LayersChanged?.Invoke(this, new LayersChangedEventArgs(ActiveLayerGuid, LayerAction.SetActive));
+                    return;
+                }
+            }
+        }
+
+        public void ToggleLayer(int index)
+        {
+            if (index < Layers.Count && index >= 0)
+            {
+                Layer layer = Layers[index];
+                if (layer.IsActive && Layers.Count(x => x.IsActive) == 1)
+                {
+                    return;
                 }
                 }
+
+                if (ActiveLayerGuid == layer.LayerGuid)
+                {
+                    SetNextSelectedLayerAsActive(layer.LayerGuid);
+                }
+
+                layer.IsActive = !layer.IsActive;
+            }
+        }
+
+        /// <summary>
+        /// Selects all layers between active layer and layer at given index.
+        /// </summary>
+        /// <param name="index">End of range index.</param>
+        public void SelectLayersRange(int index)
+        {
+            DeselectAllExcept(ActiveLayer);
+            int firstIndex = Layers.IndexOf(ActiveLayer);
+
+            int startIndex = Math.Min(index, firstIndex);
+            for (int i = startIndex; i <= startIndex + Math.Abs(index - firstIndex); i++)
+            {
+                Layers[i].IsActive = true;
+            }
+        }
+
+        public void DeselectAllExcept(Layer exceptLayer)
+        {
+            foreach (var layer in Layers)
+            {
+                if (layer == exceptLayer)
+                {
+                    continue;
+                }
+
+                layer.IsActive = false;
             }
             }
         }
         }
 
 
@@ -135,81 +217,139 @@ namespace PixiEditor.Models.DataHolders
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Merges two layers.
-        /// </summary>
-        /// <param name="firstLayer">The lower layer.</param>
-        /// <param name="secondLayer">The upper layer.</param>
-        /// <returns>The merged layer.</returns>
-        public Layer MergeLayers(Layer firstLayer, Layer secondLayer, bool nameOfSecond, int index)
+        public void RemoveLayer(Layer layer)
+        {
+            RemoveLayer(Layers.IndexOf(layer));
+        }
+
+        public void RemoveActiveLayers()
         {
         {
+            if (Layers.Count == 0 || !Layers.Any(x => x.IsActive))
+            {
+                return;
+            }
+
+            Layer[] layers = Layers.Where(x => x.IsActive).ToArray();
+            int firstIndex = Layers.IndexOf(layers[0]);
+
+            object[] guidArgs = new object[] { layers.Select(x => x.LayerGuid).ToArray() };
+
+            StorageBasedChange change = new StorageBasedChange(this, layers);
+
+            RemoveLayersProcess(guidArgs);
+
+            InjectRemoveActiveLayersUndo(guidArgs, change);
+
+            SetNextLayerAsActive(firstIndex);
+        }
+
+        public Layer MergeLayers(Layer[] layersToMerge, bool nameOfLast, int index)
+        {
+            if (layersToMerge == null || layersToMerge.Length < 2)
+            {
+                throw new ArgumentException("Not enough layers were provided to merge. Minimum amount is 2");
+            }
+
             string name;
             string name;
 
 
             // Wich name should be used
             // Wich name should be used
-            if (nameOfSecond)
+            if (nameOfLast)
             {
             {
-                name = secondLayer.Name;
+                name = layersToMerge[^1].Name;
             }
             }
             else
             else
             {
             {
-                name = firstLayer.Name;
+                name = layersToMerge[0].Name;
             }
             }
 
 
-            Layer mergedLayer = firstLayer.MergeWith(secondLayer, name, Width, Height);
+            Layer mergedLayer = layersToMerge[0];
+
+            for (int i = 0; i < layersToMerge.Length - 1; i++)
+            {
+                Layer firstLayer = mergedLayer;
+                Layer secondLayer = layersToMerge[i + 1];
+                mergedLayer = firstLayer.MergeWith(secondLayer, name, Width, Height);
+                Layers.Remove(layersToMerge[i]);
+            }
+
+            Layers.Remove(layersToMerge[^1]);
 
 
-            // Insert new layer and remove old
             Layers.Insert(index, mergedLayer);
             Layers.Insert(index, mergedLayer);
-            Layers.Remove(firstLayer);
-            Layers.Remove(secondLayer);
 
 
-            SetActiveLayer(Layers.IndexOf(mergedLayer));
+            SetMainActiveLayer(Layers.IndexOf(mergedLayer));
 
 
             return mergedLayer;
             return mergedLayer;
         }
         }
 
 
-        /// <summary>
-        /// Merges two layers.
-        /// </summary>
-        /// <param name="firstIndex">The index of the lower layer.</param>
-        /// <param name="secondIndex">The index of the upper leyer.</param>
-        /// <returns>The merged layer.</returns>
-        public Layer MergeLayers(int firstIndex, int secondIndex, bool nameOfSecond)
+        public Layer MergeLayers(Layer[] layersToMerge, bool nameIsLastLayers)
         {
         {
-            Layer firstLayer = Layers[firstIndex];
-            Layer secondLayer = Layers[secondIndex];
-
-            IEnumerable<Layer> undoArgs = new[] { firstLayer, secondLayer };
-            if (firstIndex > secondIndex)
+            if (layersToMerge == null || layersToMerge.Length < 2)
             {
             {
-                undoArgs = undoArgs.Reverse();
+                throw new ArgumentException("Not enough layers were provided to merge. Minimum amount is 2");
             }
             }
 
 
+            IEnumerable<Layer> undoArgs = layersToMerge;
+
             StorageBasedChange undoChange = new StorageBasedChange(this, undoArgs);
             StorageBasedChange undoChange = new StorageBasedChange(this, undoArgs);
 
 
-            var layer = MergeLayers(firstLayer, secondLayer, nameOfSecond, firstIndex);
+            int[] indexes = layersToMerge.Select(x => Layers.IndexOf(x)).ToArray();
+
+            var layer = MergeLayers(layersToMerge, nameIsLastLayers, Layers.IndexOf(layersToMerge[0]));
 
 
             UndoManager.AddUndoChange(undoChange.ToChange(
             UndoManager.AddUndoChange(undoChange.ToChange(
                 InsertLayersAtIndexesProcess,
                 InsertLayersAtIndexesProcess,
-                new object[] { firstIndex > secondIndex ? firstIndex - 1 : firstIndex },
+                new object[] { indexes[0] },
                 MergeLayersProcess,
                 MergeLayersProcess,
-                new object[] { firstIndex, secondIndex, nameOfSecond, layer.LayerGuid },
+                new object[] { indexes, nameIsLastLayers, layer.LayerGuid },
                 "Undo merge layers"));
                 "Undo merge layers"));
 
 
             return layer;
             return layer;
         }
         }
 
 
+        private void InjectRemoveActiveLayersUndo(object[] guidArgs, StorageBasedChange change)
+        {
+            Action<Layer[], UndoLayer[]> undoAction = RestoreLayersProcess;
+            Action<object[]> redoAction = RemoveLayersProcess;
+
+            if (Layers.Count == 0)
+            {
+                Layer layer = new Layer("Base Layer");
+                Layers.Add(layer);
+                undoAction = (Layer[] layers, UndoLayer[] undoData) =>
+                {
+                    Layers.RemoveAt(0);
+                    RestoreLayersProcess(layers, undoData);
+                };
+                redoAction = (object[] args) =>
+                {
+                    RemoveLayersProcess(args);
+                    Layers.Add(layer);
+                };
+            }
+
+            UndoManager.AddUndoChange(
+            change.ToChange(
+                undoAction,
+                redoAction,
+                guidArgs,
+                "Remove layers"));
+        }
+
         private void MergeLayersProcess(object[] args)
         private void MergeLayersProcess(object[] args)
         {
         {
-            if (args.Length > 0 
-                && args[0] is int firstIndex
-                && args[1] is int secondIndex
-                && args[2] is bool nameOfSecond
-                && args[3] is Guid mergedLayerGuid)
+            if (args.Length > 0
+                && args[0] is int[] indexes
+                && args[1] is bool nameOfSecond
+                && args[2] is Guid mergedLayerGuid)
             {
             {
-                Layer firstLayer = Layers[firstIndex];
-                Layer secondLayer = Layers[secondIndex];
+                Layer[] layers = new Layer[indexes.Length];
+
+                for (int i = 0; i < layers.Length; i++)
+                {
+                    layers[i] = Layers[indexes[i]];
+                }
 
 
-                Layer layer = MergeLayers(firstLayer, secondLayer, nameOfSecond, firstIndex);
+                Layer layer = MergeLayers(layers, nameOfSecond, indexes[0]);
                 layer.ChangeGuid(mergedLayerGuid);
                 layer.ChangeGuid(mergedLayerGuid);
             }
             }
         }
         }
@@ -222,27 +362,37 @@ namespace PixiEditor.Models.DataHolders
                 for (int i = 0; i < layers.Length; i++)
                 for (int i = 0; i < layers.Length; i++)
                 {
                 {
                     Layer layer = layers[i];
                     Layer layer = layers[i];
+                    layer.IsActive = true;
                     Layers.Insert(data[i].LayerIndex, layer);
                     Layers.Insert(data[i].LayerIndex, layer);
                 }
                 }
+
+                ActiveLayerGuid = layers.First(x => x.LayerHighlightColor == MainSelectedLayerColor).LayerGuid;
+                // Identifying main layer by highlightColor is a bit hacky, but shhh
             }
             }
         }
         }
 
 
         /// <summary>
         /// <summary>
         ///     Moves offsets of layers by specified vector.
         ///     Moves offsets of layers by specified vector.
         /// </summary>
         /// </summary>
-        private void MoveOffsets(Coordinates moveVector)
+        private void MoveOffsets(IEnumerable<Layer> layers, Coordinates moveVector)
         {
         {
-            for (int i = 0; i < Layers.Count; i++)
+            foreach (Layer layer in layers)
             {
             {
-                Thickness offset = Layers[i].Offset;
-                Layers[i].Offset = new Thickness(offset.Left + moveVector.X, offset.Top + moveVector.Y, 0, 0);
+                Thickness offset = layer.Offset;
+                layer.Offset = new Thickness(offset.Left + moveVector.X, offset.Top + moveVector.Y, 0, 0);
             }
             }
         }
         }
 
 
         private void MoveOffsetsProcess(object[] arguments)
         private void MoveOffsetsProcess(object[] arguments)
         {
         {
-            Coordinates vector = (Coordinates)arguments[0];
-            MoveOffsets(vector);
+            if (arguments.Length > 0 && arguments[0] is IEnumerable<Layer> layers && arguments[1] is Coordinates vector)
+            {
+                MoveOffsets(layers, vector);
+            }
+            else
+            {
+                throw new ArgumentException("Provided arguments were invalid. Expected IEnumerable<Layer> and Coordinates");
+            }
         }
         }
 
 
         private void MoveLayerProcess(object[] parameter)
         private void MoveLayerProcess(object[] parameter)
@@ -251,9 +401,9 @@ namespace PixiEditor.Models.DataHolders
             int amount = (int)parameter[1];
             int amount = (int)parameter[1];
 
 
             Layers.Move(layerIndex, layerIndex + amount);
             Layers.Move(layerIndex, layerIndex + amount);
-            if (ActiveLayerIndex == layerIndex)
+            if (Layers.IndexOf(ActiveLayer) == layerIndex)
             {
             {
-                SetActiveLayer(layerIndex + amount);
+                SetMainActiveLayer(layerIndex + amount);
             }
             }
         }
         }
 
 
@@ -266,7 +416,7 @@ namespace PixiEditor.Models.DataHolders
                 Layers.Insert(layersData[i].LayerIndex, layer);
                 Layers.Insert(layersData[i].LayerIndex, layer);
                 if (layersData[i].IsActive)
                 if (layersData[i].IsActive)
                 {
                 {
-                    SetActiveLayer(Layers.IndexOf(layer));
+                    SetMainActiveLayer(Layers.IndexOf(layer));
                 }
                 }
             }
             }
         }
         }
@@ -280,11 +430,24 @@ namespace PixiEditor.Models.DataHolders
                 bool wasActive = layer.IsActive;
                 bool wasActive = layer.IsActive;
                 Layers.Remove(layer);
                 Layers.Remove(layer);
 
 
-                if (wasActive || ActiveLayerIndex >= index)
+                if (wasActive || Layers.IndexOf(ActiveLayer) >= index)
                 {
                 {
                     SetNextLayerAsActive(index);
                     SetNextLayerAsActive(index);
                 }
                 }
             }
             }
         }
         }
+
+        private void RemoveLayersProcess(object[] parameters)
+        {
+            if (parameters != null && parameters.Length > 0 && parameters[0] is IEnumerable<Guid> layerGuids)
+            {
+                object[] args = new object[1];
+                foreach (var guid in layerGuids)
+                {
+                    args[0] = guid;
+                    RemoveLayerProcess(args);
+                }
+            }
+        }
     }
     }
 }
 }

+ 32 - 19
PixiEditor/Models/DataHolders/Document/Document.cs

@@ -1,5 +1,7 @@
 using System;
 using System;
 using System.Buffers;
 using System.Buffers;
+using System.Collections;
+using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
@@ -154,7 +156,7 @@ namespace PixiEditor.Models.DataHolders
         /// </summary>
         /// </summary>
         public void ClipCanvas()
         public void ClipCanvas()
         {
         {
-            DoubleCords points = GetEdgePoints();
+            DoubleCords points = GetEdgePoints(Layers);
             int smallestX = points.Coords1.X;
             int smallestX = points.Coords1.X;
             int smallestY = points.Coords1.Y;
             int smallestY = points.Coords1.Y;
             int biggestX = points.Coords2.X;
             int biggestX = points.Coords2.X;
@@ -173,7 +175,7 @@ namespace PixiEditor.Models.DataHolders
             int oldWidth = Width;
             int oldWidth = Width;
             int oldHeight = Height;
             int oldHeight = Height;
 
 
-            MoveOffsets(moveVector);
+            MoveOffsets(Layers, moveVector);
             Width = width;
             Width = width;
             Height = height;
             Height = height;
 
 
@@ -189,11 +191,17 @@ namespace PixiEditor.Models.DataHolders
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Centers content inside document.
+        /// Centers selected, visible layers inside document.
         /// </summary>
         /// </summary>
         public void CenterContent()
         public void CenterContent()
         {
         {
-            DoubleCords points = GetEdgePoints();
+            var layersToCenter = Layers.Where(x => x.IsActive && x.IsVisible);
+            if (layersToCenter.Count() == 0)
+            {
+                return;
+            }
+
+            DoubleCords points = GetEdgePoints(layersToCenter);
 
 
             int smallestX = points.Coords1.X;
             int smallestX = points.Coords1.X;
             int smallestY = points.Coords1.Y;
             int smallestY = points.Coords1.Y;
@@ -211,13 +219,13 @@ namespace PixiEditor.Models.DataHolders
                 new Coordinates(Width, Height));
                 new Coordinates(Width, Height));
             Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
             Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
 
 
-            MoveOffsets(moveVector);
+            MoveOffsets(layersToCenter, moveVector);
             UndoManager.AddUndoChange(
             UndoManager.AddUndoChange(
                 new Change(
                 new Change(
                     MoveOffsetsProcess,
                     MoveOffsetsProcess,
-                    new object[] { new Coordinates(-moveVector.X, -moveVector.Y) },
+                    new object[] { layersToCenter, new Coordinates(-moveVector.X, -moveVector.Y) },
                     MoveOffsetsProcess,
                     MoveOffsetsProcess,
-                    new object[] { moveVector },
+                    new object[] { layersToCenter, moveVector },
                     "Center content"));
                     "Center content"));
         }
         }
 
 
@@ -266,35 +274,40 @@ namespace PixiEditor.Models.DataHolders
             return 0;
             return 0;
         }
         }
 
 
-        private DoubleCords GetEdgePoints()
+        private DoubleCords GetEdgePoints(IEnumerable<Layer> layers)
         {
         {
-            Layer firstLayer = Layers[0];
+            if (Layers.Count == 0)
+            {
+                throw new ArgumentException("Not enough layers");
+            }
+
+            Layer firstLayer = layers.First();
             int smallestX = firstLayer.OffsetX;
             int smallestX = firstLayer.OffsetX;
             int smallestY = firstLayer.OffsetY;
             int smallestY = firstLayer.OffsetY;
             int biggestX = smallestX + firstLayer.Width;
             int biggestX = smallestX + firstLayer.Width;
             int biggestY = smallestY + firstLayer.Height;
             int biggestY = smallestY + firstLayer.Height;
 
 
-            for (int i = 0; i < Layers.Count; i++)
+            foreach (Layer layer in layers)
             {
             {
-                Layers[i].ClipCanvas();
-                if (Layers[i].OffsetX < smallestX)
+                layer.ClipCanvas();
+                if (layer.OffsetX < smallestX)
                 {
                 {
-                    smallestX = Layers[i].OffsetX;
+                    smallestX = layer.OffsetX;
                 }
                 }
 
 
-                if (Layers[i].OffsetX + Layers[i].Width > biggestX)
+                if (layer.OffsetX + layer.Width > biggestX)
                 {
                 {
-                    biggestX = Layers[i].OffsetX + Layers[i].Width;
+                    biggestX = layer.OffsetX + layer.Width;
                 }
                 }
 
 
-                if (Layers[i].OffsetY < smallestY)
+                if (layer.OffsetY < smallestY)
                 {
                 {
-                    smallestY = Layers[i].OffsetY;
+                    smallestY = layer.OffsetY;
                 }
                 }
 
 
-                if (Layers[i].OffsetY + Layers[i].Height > biggestY)
+                if (layer.OffsetY + layer.Height > biggestY)
                 {
                 {
-                    biggestY = Layers[i].OffsetY + Layers[i].Height;
+                    biggestY = layer.OffsetY + layer.Height;
                 }
                 }
             }
             }
 
 

+ 1 - 1
PixiEditor/Models/DataHolders/Selection.cs

@@ -58,7 +58,7 @@ namespace PixiEditor.Models.DataHolders
 
 
         public void Clear()
         public void Clear()
         {
         {
-            SelectionLayer = new Layer("_selectionLayer");
+            SelectionLayer.Clear();
             SelectedPoints.Clear();
             SelectedPoints.Clear();
         }
         }
     }
     }

+ 4 - 1
PixiEditor/Models/Events/DocumentChangedEventArgs.cs

@@ -4,11 +4,14 @@ namespace PixiEditor.Models.Events
 {
 {
     public class DocumentChangedEventArgs
     public class DocumentChangedEventArgs
     {
     {
-        public DocumentChangedEventArgs(Document newDocument)
+        public DocumentChangedEventArgs(Document newDocument, Document oldDocument)
         {
         {
             NewDocument = newDocument;
             NewDocument = newDocument;
+            OldDocument = oldDocument;
         }
         }
 
 
+        public Document OldDocument { get; set; }
+
         public Document NewDocument { get; set; }
         public Document NewDocument { get; set; }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -108,7 +108,7 @@ namespace PixiEditor.Models.ImageManipulation
 
 
         public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
         public static Dictionary<Guid, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
         {
         {
-            Dictionary<Guid, Color[]> result = new Dictionary<Guid, Color[]>();
+            Dictionary<Guid, Color[]> result = new ();
 
 
             foreach (Layer layer in layers)
             foreach (Layer layer in layers)
             {
             {

+ 12 - 1
PixiEditor/Models/Layers/Layer.cs

@@ -5,7 +5,6 @@ using System.Linq;
 using System.Windows;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
-using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
@@ -31,6 +30,8 @@ namespace PixiEditor.Models.Layers
 
 
         private float opacity = 1f;
         private float opacity = 1f;
 
 
+        private string layerHighlightColor = "#666666";
+
         public Layer(string name)
         public Layer(string name)
         {
         {
             Name = name;
             Name = name;
@@ -60,6 +61,15 @@ namespace PixiEditor.Models.Layers
 
 
         public Dictionary<Coordinates, Color> LastRelativeCoordinates { get; set; }
         public Dictionary<Coordinates, Color> LastRelativeCoordinates { get; set; }
 
 
+        public string LayerHighlightColor
+        {
+            get => IsActive ? layerHighlightColor : "#00000000";
+            set
+            {
+                SetProperty(ref layerHighlightColor, value);
+            }
+        }
+
         public string Name
         public string Name
         {
         {
             get => name;
             get => name;
@@ -77,6 +87,7 @@ namespace PixiEditor.Models.Layers
             {
             {
                 isActive = value;
                 isActive = value;
                 RaisePropertyChanged(nameof(IsActive));
                 RaisePropertyChanged(nameof(IsActive));
+                RaisePropertyChanged(nameof(LayerHighlightColor));
             }
             }
         }
         }
 
 

+ 5 - 0
PixiEditor/Models/Tools/Tool.cs

@@ -2,6 +2,7 @@
 using System.Windows.Input;
 using System.Windows.Input;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 
 
@@ -74,6 +75,10 @@ namespace PixiEditor.Models.Tools
         {
         {
         }
         }
 
 
+        public virtual void OnStart(Coordinates clickPosition)
+        {
+        }
+
         public virtual void OnRecordingLeftMouseDown(MouseEventArgs e)
         public virtual void OnRecordingLeftMouseDown(MouseEventArgs e)
         {
         {
         }
         }

+ 69 - 60
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -23,8 +23,8 @@ namespace PixiEditor.Models.Tools.Tools
         private Dictionary<Guid, bool> clearedPixels = new Dictionary<Guid, bool>();
         private Dictionary<Guid, bool> clearedPixels = new Dictionary<Guid, bool>();
         private Coordinates[] currentSelection;
         private Coordinates[] currentSelection;
         private Coordinates lastMouseMove;
         private Coordinates lastMouseMove;
-        private Coordinates lastStartMousePos;
         private Dictionary<Guid, Color[]> startPixelColors;
         private Dictionary<Guid, Color[]> startPixelColors;
+        private Dictionary<Guid, Color[]> endPixelColors;
         private Dictionary<Guid, Thickness> startingOffsets;
         private Dictionary<Guid, Thickness> startingOffsets;
         private Coordinates[] startSelection;
         private Coordinates[] startSelection;
         private bool updateViewModelSelection = true;
         private bool updateViewModelSelection = true;
@@ -58,23 +58,31 @@ namespace PixiEditor.Models.Tools.Tools
         }
         }
 
 
         public override void AfterAddedUndo(UndoManager undoManager)
         public override void AfterAddedUndo(UndoManager undoManager)
-        {
-            if (currentSelection != null && currentSelection.Length != 0)
-            {
-                // Inject to default undo system change custom changes made by this tool
-                foreach (var item in startPixelColors)
-                {
-                    BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(startSelection, item.Value);
-                    Change changes = undoManager.UndoStack.Peek();
-                    Guid layerGuid = item.Key;
-
-                    ((LayerChange[])changes.OldValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels
-                        .AddRangeOverride(beforeMovePixels.ChangedPixels);
-
-                    ((LayerChange[])changes.NewValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels
-                        .AddRangeNewOnly(BitmapPixelChanges
-                            .FromSingleColoredArray(startSelection, System.Windows.Media.Colors.Transparent)
-                            .ChangedPixels);
+        {
+            if (currentSelection != null && currentSelection.Length > 0)
+            {
+                Change changes = undoManager.UndoStack.Peek();
+
+                // Inject to default undo system change custom changes made by this tool
+                foreach (var item in startPixelColors)
+                {
+                    BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(startSelection, item.Value);
+                    BitmapPixelChanges afterMovePixels = BitmapPixelChanges.FromArrays(currentSelection, endPixelColors[item.Key]);
+                    Guid layerGuid = item.Key;
+                    var oldValue = (LayerChange[])changes.OldValue;
+
+                    if (oldValue.Any(x => x.LayerGuid == layerGuid))
+                    {
+                        var layer = oldValue.First(x => x.LayerGuid == layerGuid);
+                        layer.PixelChanges.ChangedPixels.AddRangeOverride(afterMovePixels.ChangedPixels);
+                        layer.PixelChanges.ChangedPixels
+                            .AddRangeOverride(beforeMovePixels.ChangedPixels);
+
+                        ((LayerChange[])changes.NewValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels
+                            .AddRangeNewOnly(BitmapPixelChanges
+                                .FromSingleColoredArray(startSelection, System.Windows.Media.Colors.Transparent)
+                                .ChangedPixels);
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -94,48 +102,50 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
         }
         }
 
 
-        public override LayerChange[] Use(Layer layer, Coordinates[] mouseMove, Color color)
-        {
-            Coordinates start = mouseMove[^1];
+        public override void OnStart(Coordinates startPos)
+        {
+            ResetSelectionValues(startPos);
 
 
-            // I am aware that this could be moved to OnMouseDown, but it is executed before Use, so I didn't want to complicate for now
-            if (lastStartMousePos != start)
-            {
-                ResetSelectionValues(start);
-
-                // Move offset if no selection
-                Selection selection = ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection;
-                if (selection != null && selection.SelectedPoints.Count > 0)
-                {
-                    currentSelection = selection.SelectedPoints.ToArray();
-                }
-                else
-                {
-                    currentSelection = Array.Empty<Coordinates>();
-                }
-
-                if (Keyboard.IsKeyDown(Key.LeftCtrl) || MoveAll)
-                {
-                    affectedLayers = ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Where(x => x.IsVisible)
-                        .ToArray();
-                }
-                else
-                {
-                    affectedLayers = new[] { layer };
-                }
+            // Move offset if no selection
+            Selection selection = ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection;
+            if (selection != null && selection.SelectedPoints.Count > 0)
+            {
+                currentSelection = selection.SelectedPoints.ToArray();
+            }
+            else
+            {
+                currentSelection = Array.Empty<Coordinates>();
+            }
 
 
-                startSelection = currentSelection;
-                startPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, startSelection);
-                startingOffsets = GetOffsets(affectedLayers);
-            }
+            if (Keyboard.IsKeyDown(Key.LeftCtrl) || MoveAll)
+            {
+                affectedLayers = ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Where(x => x.IsVisible)
+                    .ToArray();
+            }
+            else
+            {
+                affectedLayers = ViewModelMain.Current.BitmapManager.ActiveDocument
+                    .Layers.Where(x => x.IsActive && x.IsVisible).ToArray();
+            }
+
+            startSelection = currentSelection;
+            startPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, startSelection);
+            startingOffsets = GetOffsets(affectedLayers);
+        }
 
 
+        public override LayerChange[] Use(Layer layer, Coordinates[] mouseMove, Color color)
+        {
             LayerChange[] result = new LayerChange[affectedLayers.Length];
             LayerChange[] result = new LayerChange[affectedLayers.Length];
-            var end = mouseMove[0];
+            var end = mouseMove[0];
+            var lastSelection = currentSelection.ToArray();
             for (int i = 0; i < affectedLayers.Length; i++)
             for (int i = 0; i < affectedLayers.Length; i++)
             {
             {
                 if (currentSelection.Length > 0)
                 if (currentSelection.Length > 0)
                 {
                 {
-                    var changes = MoveSelection(affectedLayers[i], mouseMove);
+                    endPixelColors = BitmapUtils.GetPixelsForSelection(affectedLayers, currentSelection);
+                    var changes = MoveSelection(affectedLayers[i], mouseMove);
+                    ClearSelectedPixels(affectedLayers[i], lastSelection);
+
                     changes = RemoveTransparentPixels(changes);
                     changes = RemoveTransparentPixels(changes);
 
 
                     result[i] = new LayerChange(changes, affectedLayers[i]);
                     result[i] = new LayerChange(changes, affectedLayers[i]);
@@ -157,13 +167,11 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             Coordinates end = mouseMove[0];
             Coordinates end = mouseMove[0];
 
 
-            currentSelection = TranslateSelection(end, out Coordinates[] previousSelection);
+            currentSelection = TranslateSelection(end);
             if (updateViewModelSelection)
             if (updateViewModelSelection)
             {
             {
                 ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
                 ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
-            }
-
-            ClearSelectedPixels(layer, previousSelection);
+            }
 
 
             lastMouseMove = end;
             lastMouseMove = end;
             return BitmapPixelChanges.FromArrays(currentSelection, startPixelColors[layer.LayerGuid]);
             return BitmapPixelChanges.FromArrays(currentSelection, startPixelColors[layer.LayerGuid]);
@@ -203,19 +211,20 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         private void ResetSelectionValues(Coordinates start)
         private void ResetSelectionValues(Coordinates start)
         {
         {
-            lastStartMousePos = start;
             lastMouseMove = start;
             lastMouseMove = start;
             clearedPixels = new Dictionary<Guid, bool>();
             clearedPixels = new Dictionary<Guid, bool>();
+            endPixelColors = new Dictionary<Guid, Color[]>();
+            currentSelection = null;
+            affectedLayers = null;
             updateViewModelSelection = true;
             updateViewModelSelection = true;
             startPixelColors = null;
             startPixelColors = null;
             startSelection = null;
             startSelection = null;
         }
         }
 
 
-        private Coordinates[] TranslateSelection(Coordinates end, out Coordinates[] previousSelection)
+        private Coordinates[] TranslateSelection(Coordinates end)
         {
         {
             Coordinates translation = Transform.GetTranslation(lastMouseMove, end);
             Coordinates translation = Transform.GetTranslation(lastMouseMove, end);
-            previousSelection = currentSelection.ToArray();
-            return Transform.Translate(previousSelection, translation);
+            return Transform.Translate(currentSelection, translation);
         }
         }
 
 
         private void ClearSelectedPixels(Layer layer, Coordinates[] selection)
         private void ClearSelectedPixels(Layer layer, Coordinates[] selection)

+ 89 - 89
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -1,90 +1,90 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Windows.Controls;
-using System.Windows.Input;
-using PixiEditor.Helpers;
-using PixiEditor.Helpers.Extensions;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Enums;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
-using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class SelectTool : ReadonlyTool
-    {
-        private IEnumerable<Coordinates> oldSelectedPoints;
-
-        private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
-
-        public SelectTool()
-        {
-            ActionDisplay = "Click and move to select an area.";
-            Tooltip = "Selects area. (M)";
-            Toolbar = new SelectToolToolbar();
-        }
-
-        public SelectionType SelectionType { get; set; } = SelectionType.Add;
-
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
-            SelectionType = Toolbar.GetEnumSetting<SelectionType>("SelectMode").Value;
-
-            oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
-        }
-
-        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
-        {
-            if (ActiveSelection.SelectedPoints.Count <= 1)
-            {
-                // If we have not selected multiple points, clear the selection
-                ActiveSelection.Clear();
-            }
-
-            SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelectedPoints, SelectionType);
-        }
-
-        public override void Use(Coordinates[] pixels)
-        {
-            Select(pixels);
-        }
-
-        public IEnumerable<Coordinates> GetRectangleSelectionForPoints(Coordinates start, Coordinates end)
-        {
-            RectangleTool rectangleTool = new RectangleTool();
-            List<Coordinates> selection = rectangleTool.CreateRectangle(start, end, 1).ToList();
-            selection.AddRange(rectangleTool.CalculateFillForRectangle(start, end, 1));
-            return selection;
-        }
-
-        /// <summary>
-        ///     Gets coordinates of every pixel in root layer.
-        /// </summary>
-        /// <returns>Coordinates array of pixels.</returns>
-        public IEnumerable<Coordinates> GetAllSelection()
-        {
-            return GetAllSelection(ViewModelMain.Current.BitmapManager.ActiveDocument);
-        }
-
-        /// <summary>
-        ///     Gets coordinates of every pixel in chosen document.
-        /// </summary>
-        /// <returns>Coordinates array of pixels.</returns>
-        public IEnumerable<Coordinates> GetAllSelection(Document document)
-        {
-            return GetRectangleSelectionForPoints(new Coordinates(0, 0), new Coordinates(document.Width - 1, document.Height - 1));
-        }
-
-        private void Select(Coordinates[] pixels)
-        {
-            IEnumerable<Coordinates> selection = GetRectangleSelectionForPoints(pixels[^1], pixels[0]);
-            ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selection, SelectionType);
-        }
-    }
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows.Controls;
+using System.Windows.Input;
+using PixiEditor.Helpers;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
+using PixiEditor.Models.Tools.ToolSettings.Toolbars;
+using PixiEditor.Models.Undo;
+using PixiEditor.ViewModels;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class SelectTool : ReadonlyTool
+    {
+        private IEnumerable<Coordinates> oldSelectedPoints;
+
+        private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
+
+        public SelectTool()
+        {
+            ActionDisplay = "Click and move to select an area.";
+            Tooltip = "Selects area. (M)";
+            Toolbar = new SelectToolToolbar();
+        }
+
+        public SelectionType SelectionType { get; set; } = SelectionType.Add;
+
+        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
+        {
+            SelectionType = Toolbar.GetEnumSetting<SelectionType>("SelectMode").Value;
+
+            oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
+        }
+
+        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
+        {
+            if (ActiveSelection.SelectedPoints.Count <= 1)
+            {
+                // If we have not selected multiple points, clear the selection
+                ActiveSelection.Clear();
+            }
+
+            SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelectedPoints, SelectionType);
+        }
+
+        public override void Use(Coordinates[] pixels)
+        {
+            Select(pixels);
+        }
+
+        public IEnumerable<Coordinates> GetRectangleSelectionForPoints(Coordinates start, Coordinates end)
+        {
+            RectangleTool rectangleTool = new RectangleTool();
+            List<Coordinates> selection = rectangleTool.CreateRectangle(start, end, 1).ToList();
+            selection.AddRange(rectangleTool.CalculateFillForRectangle(start, end, 1));
+            return selection;
+        }
+
+        /// <summary>
+        ///     Gets coordinates of every pixel in root layer.
+        /// </summary>
+        /// <returns>Coordinates array of pixels.</returns>
+        public IEnumerable<Coordinates> GetAllSelection()
+        {
+            return GetAllSelection(ViewModelMain.Current.BitmapManager.ActiveDocument);
+        }
+
+        /// <summary>
+        ///     Gets coordinates of every pixel in chosen document.
+        /// </summary>
+        /// <returns>Coordinates array of pixels.</returns>
+        public IEnumerable<Coordinates> GetAllSelection(Document document)
+        {
+            return GetRectangleSelectionForPoints(new Coordinates(0, 0), new Coordinates(document.Width - 1, document.Height - 1));
+        }
+
+        private void Select(Coordinates[] pixels)
+        {
+            IEnumerable<Coordinates> selection = GetRectangleSelectionForPoints(pixels[^1], pixels[0]);
+            ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selection, SelectionType);
+        }
+    }
 }
 }

+ 1 - 0
PixiEditor/Models/Undo/StorageBasedChange.cs

@@ -90,6 +90,7 @@ namespace PixiEditor.Models.Undo
                     IsActive = storedLayer.IsActive,
                     IsActive = storedLayer.IsActive,
                     Width = storedLayer.Width,
                     Width = storedLayer.Width,
                     Height = storedLayer.Height,
                     Height = storedLayer.Height,
+                    LayerHighlightColor = storedLayer.LayerHighlightColor                    
                 };
                 };
                 layers[i].ChangeGuid(storedLayer.LayerGuid);
                 layers[i].ChangeGuid(storedLayer.LayerGuid);
 
 

+ 3 - 0
PixiEditor/Models/Undo/UndoLayer.cs

@@ -10,6 +10,8 @@ namespace PixiEditor.Models.Undo
 
 
         public Guid LayerGuid { get; init; }
         public Guid LayerGuid { get; init; }
 
 
+        public string LayerHighlightColor { get; set; }
+
         public string Name { get; set; }
         public string Name { get; set; }
 
 
         public int LayerIndex { get; set; }
         public int LayerIndex { get; set; }
@@ -47,6 +49,7 @@ namespace PixiEditor.Models.Undo
             Opacity = layer.Opacity;
             Opacity = layer.Opacity;
             IsActive = layer.IsActive;
             IsActive = layer.IsActive;
             LayerGuid = layer.LayerGuid;
             LayerGuid = layer.LayerGuid;
+            LayerHighlightColor = layer.LayerHighlightColor;
         }
         }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/Styles/ComboBoxDarkStyle.xaml

@@ -2,7 +2,7 @@
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2">
                     xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2">
 
 
-    <SolidColorBrush x:Key="ComboBox.Static.Background" Color="#333333" />
+    <SolidColorBrush x:Key="ComboBox.Static.Background" Color="#252525" />
     <SolidColorBrush x:Key="ComboBox.Static.Border" Color="#2F2F37" />
     <SolidColorBrush x:Key="ComboBox.Static.Border" Color="#2F2F37" />
     <SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060" />
     <SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060" />
     <SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#333333" />
     <SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#333333" />

+ 1 - 1
PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs

@@ -56,7 +56,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         private void Copy(object parameter)
         private void Copy(object parameter)
         {
         {
             ClipboardController.CopyToClipboard(
             ClipboardController.CopyToClipboard(
-                new[] { Owner.BitmapManager.ActiveDocument.ActiveLayer },
+                Owner.BitmapManager.ActiveDocument.Layers.Where(x => x.IsActive && x.IsVisible).ToArray(),
                 Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints.ToArray(),
                 Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints.ToArray(),
                 Owner.BitmapManager.ActiveDocument.Width,
                 Owner.BitmapManager.ActiveDocument.Width,
                 Owner.BitmapManager.ActiveDocument.Height);
                 Owner.BitmapManager.ActiveDocument.Height);

+ 1 - 1
PixiEditor/ViewModels/SubViewModels/Main/DocumentViewModel.cs

@@ -53,7 +53,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         private void DeletePixels(object parameter)
         private void DeletePixels(object parameter)
         {
         {
             Owner.BitmapManager.BitmapOperations.DeletePixels(
             Owner.BitmapManager.BitmapOperations.DeletePixels(
-                new[] { Owner.BitmapManager.ActiveLayer },
+                Owner.BitmapManager.ActiveDocument.Layers.Where(x => x.IsActive && x.IsVisible).ToArray(),
                 Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints.ToArray());
                 Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints.ToArray());
         }
         }
 
 

+ 87 - 16
PixiEditor/ViewModels/SubViewModels/Main/LayersViewModel.cs

@@ -1,4 +1,9 @@
-using PixiEditor.Helpers;
+using System;
+using System.Linq;
+using System.Windows.Input;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Layers;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
@@ -8,7 +13,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public RelayCommand NewLayerCommand { get; set; }
         public RelayCommand NewLayerCommand { get; set; }
 
 
-        public RelayCommand DeleteLayerCommand { get; set; }
+        public RelayCommand DeleteLayersCommand { get; set; }
 
 
         public RelayCommand RenameLayerCommand { get; set; }
         public RelayCommand RenameLayerCommand { get; set; }
 
 
@@ -16,6 +21,8 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public RelayCommand MoveToFrontCommand { get; set; }
         public RelayCommand MoveToFrontCommand { get; set; }
 
 
+        public RelayCommand MergeSelectedCommand { get; set; }
+
         public RelayCommand MergeWithAboveCommand { get; set; }
         public RelayCommand MergeWithAboveCommand { get; set; }
 
 
         public RelayCommand MergeWithBelowCommand { get; set; }
         public RelayCommand MergeWithBelowCommand { get; set; }
@@ -25,12 +32,19 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         {
         {
             SetActiveLayerCommand = new RelayCommand(SetActiveLayer);
             SetActiveLayerCommand = new RelayCommand(SetActiveLayer);
             NewLayerCommand = new RelayCommand(NewLayer, CanCreateNewLayer);
             NewLayerCommand = new RelayCommand(NewLayer, CanCreateNewLayer);
-            DeleteLayerCommand = new RelayCommand(DeleteLayer, CanDeleteLayer);
+            DeleteLayersCommand = new RelayCommand(DeleteLayer, CanDeleteLayer);
             MoveToBackCommand = new RelayCommand(MoveLayerToBack, CanMoveToBack);
             MoveToBackCommand = new RelayCommand(MoveLayerToBack, CanMoveToBack);
             MoveToFrontCommand = new RelayCommand(MoveLayerToFront, CanMoveToFront);
             MoveToFrontCommand = new RelayCommand(MoveLayerToFront, CanMoveToFront);
             RenameLayerCommand = new RelayCommand(RenameLayer);
             RenameLayerCommand = new RelayCommand(RenameLayer);
+            MergeSelectedCommand = new RelayCommand(MergeSelected, CanMergeSelected);
             MergeWithAboveCommand = new RelayCommand(MergeWithAbove, CanMergeWithAbove);
             MergeWithAboveCommand = new RelayCommand(MergeWithAbove, CanMergeWithAbove);
             MergeWithBelowCommand = new RelayCommand(MergeWithBelow, CanMergeWithBelow);
             MergeWithBelowCommand = new RelayCommand(MergeWithBelow, CanMergeWithBelow);
+            Owner.BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
+        }
+
+        public bool CanMergeSelected(object obj)
+        {
+            return Owner.BitmapManager.ActiveDocument?.Layers.Count(x => x.IsActive) > 1;
         }
         }
 
 
         public void NewLayer(object parameter)
         public void NewLayer(object parameter)
@@ -40,17 +54,43 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public bool CanCreateNewLayer(object parameter)
         public bool CanCreateNewLayer(object parameter)
         {
         {
-            return Owner.BitmapManager.ActiveDocument != null && Owner.BitmapManager.ActiveDocument.Layers.Count > 0;
+            return Owner.BitmapManager.ActiveDocument != null;
         }
         }
 
 
         public void SetActiveLayer(object parameter)
         public void SetActiveLayer(object parameter)
         {
         {
-            Owner.BitmapManager.ActiveDocument.SetActiveLayer((int)parameter);
+            int index = (int)parameter;
+
+            if (Owner.BitmapManager.ActiveDocument.Layers[index].IsActive && Mouse.RightButton == MouseButtonState.Pressed)
+            {
+                return;
+            }
+
+            if (Keyboard.IsKeyDown(Key.LeftCtrl))
+            {
+                Owner.BitmapManager.ActiveDocument.ToggleLayer(index);
+            }
+            else if (Keyboard.IsKeyDown(Key.LeftShift) && Owner.BitmapManager.ActiveDocument.Layers.Any(x => x.IsActive))
+            {
+                Owner.BitmapManager.ActiveDocument.SelectLayersRange(index);
+            }
+            else
+            {
+                Owner.BitmapManager.ActiveDocument.SetMainActiveLayer(index);
+            }
         }
         }
 
 
         public void DeleteLayer(object parameter)
         public void DeleteLayer(object parameter)
         {
         {
-            Owner.BitmapManager.ActiveDocument.RemoveLayer((int)parameter);
+            int index = (int)parameter;
+            if (!Owner.BitmapManager.ActiveDocument.Layers[index].IsActive)
+            {
+                Owner.BitmapManager.ActiveDocument.RemoveLayer(index);
+            }
+            else
+            {
+                Owner.BitmapManager.ActiveDocument.RemoveActiveLayers();
+            }
         }
         }
 
 
         public bool CanDeleteLayer(object property)
         public bool CanDeleteLayer(object property)
@@ -64,10 +104,10 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
             if (index == null)
             if (index == null)
             {
             {
-                index = Owner.BitmapManager.ActiveDocument.ActiveLayerIndex;
+                index = Owner.BitmapManager.ActiveDocument.Layers.IndexOf(Owner.BitmapManager.ActiveDocument.ActiveLayer);
             }
             }
 
 
-            Owner.BitmapManager.ActiveDocument.Layers[index.Value].IsRenaming = true;
+            Owner.BitmapManager.ActiveDocument.Layers[(int)index].IsRenaming = true;
         }
         }
 
 
         public bool CanRenameLayer(object parameter)
         public bool CanRenameLayer(object parameter)
@@ -97,28 +137,59 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             return (int)property > 0;
             return (int)property > 0;
         }
         }
 
 
+        public void MergeSelected(object parameter)
+        {
+            Owner.BitmapManager.ActiveDocument.MergeLayers(Owner.BitmapManager.ActiveDocument.Layers.Where(x => x.IsActive).ToArray(), false);
+        }
+
         public void MergeWithAbove(object parameter)
         public void MergeWithAbove(object parameter)
         {
         {
             int index = (int)parameter;
             int index = (int)parameter;
-            Owner.BitmapManager.ActiveDocument.MergeLayers(index, index + 1, false);
+            Layer layer1 = Owner.BitmapManager.ActiveDocument.Layers[index];
+            Layer layer2 = Owner.BitmapManager.ActiveDocument.Layers[index + 1];
+            Owner.BitmapManager.ActiveDocument.MergeLayers(new Layer[] { layer1, layer2 }, false);
         }
         }
 
 
         public void MergeWithBelow(object parameter)
         public void MergeWithBelow(object parameter)
         {
         {
             int index = (int)parameter;
             int index = (int)parameter;
-            Owner.BitmapManager.ActiveDocument.MergeLayers(index - 1, index, true);
+            Layer layer1 = Owner.BitmapManager.ActiveDocument.Layers[index - 1];
+            Layer layer2 = Owner.BitmapManager.ActiveDocument.Layers[index];
+            Owner.BitmapManager.ActiveDocument.MergeLayers(new Layer[] { layer1, layer2 }, true);
+        }
+
+        public bool CanMergeWithAbove(object property)
+        {
+            int index = (int)property;
+            return Owner.DocumentIsNotNull(null) && index != Owner.BitmapManager.ActiveDocument.Layers.Count - 1
+                && Owner.BitmapManager.ActiveDocument.Layers.Count(x => x.IsActive) == 1;
+        }
+
+        public bool CanMergeWithBelow(object property)
+        {
+            int index = (int)property;
+            return Owner.DocumentIsNotNull(null) && index != 0 && Owner.BitmapManager.ActiveDocument.Layers.Count(x => x.IsActive) == 1;
         }
         }
 
 
-        public bool CanMergeWithAbove(object propery)
+        private void BitmapManager_DocumentChanged(object sender, Models.Events.DocumentChangedEventArgs e)
         {
         {
-            int index = (int)propery;
-            return Owner.DocumentIsNotNull(null) && index != Owner.BitmapManager.ActiveDocument.Layers.Count - 1;
+            if (e.OldDocument != null)
+            {
+                e.OldDocument.LayersChanged -= Document_LayersChanged;
+            }
+
+            if (e.NewDocument != null)
+            {
+                e.NewDocument.LayersChanged += Document_LayersChanged;
+            }
         }
         }
 
 
-        public bool CanMergeWithBelow(object propery)
+        private void Document_LayersChanged(object sender, LayersChangedEventArgs e)
         {
         {
-            int index = (int)propery;
-            return Owner.DocumentIsNotNull(null) && index != 0;
+            if (e.LayerChangeType == Models.Enums.LayerAction.SetActive)
+            {
+                Owner.BitmapManager.ActiveDocument.UpdateLayersColor();
+            }
         }
         }
     }
     }
 }
 }

+ 1 - 1
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -42,7 +42,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 new CircleTool(), new RectangleTool(), new EraserTool(), new ColorPickerTool(), new BrightnessTool(),
                 new CircleTool(), new RectangleTool(), new EraserTool(), new ColorPickerTool(), new BrightnessTool(),
                 new ZoomTool()
                 new ZoomTool()
             };
             };
-            SetActiveTool(typeof(MoveTool));
+            SetActiveTool(typeof(MoveViewportTool));
         }
         }
 
 
         public void SetActiveTool<T>()
         public void SetActiveTool<T>()

+ 2 - 3
PixiEditor/ViewModels/ViewModelMain.cs

@@ -12,7 +12,6 @@ using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Events;
-using PixiEditor.Models.IO;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
@@ -161,7 +160,7 @@ namespace PixiEditor.ViewModels
                     new Shortcut(Key.N, FileSubViewModel.OpenNewFilePopupCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.N, FileSubViewModel.OpenNewFilePopupCommand, modifier: ModifierKeys.Control),
 
 
                     // Layers
                     // Layers
-                    new Shortcut(Key.F2, LayersSubViewModel.RenameLayerCommand, BitmapManager.ActiveDocument?.ActiveLayerIndex),
+                    new Shortcut(Key.F2, LayersSubViewModel.RenameLayerCommand, BitmapManager.ActiveDocument?.ActiveLayerGuid),
 
 
                     // View
                     // View
                     new Shortcut(Key.OemTilde, ViewportSubViewModel.ToggleGridLinesCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.OemTilde, ViewportSubViewModel.ToggleGridLinesCommand, modifier: ModifierKeys.Control),
@@ -194,7 +193,7 @@ namespace PixiEditor.ViewModels
             return new Shortcut(key, ToolsSubViewModel.SelectToolCommand, typeof(T), modifier);
             return new Shortcut(key, ToolsSubViewModel.SelectToolCommand, typeof(T), modifier);
         }
         }
 
 
-        private void CloseWindow(object property)
+        public void CloseWindow(object property)
         {
         {
             if (!(property is CancelEventArgs))
             if (!(property is CancelEventArgs))
             {
             {

+ 438 - 435
PixiEditor/Views/MainWindow.xaml

@@ -1,436 +1,439 @@
-<Window x:Class="PixiEditor.MainWindow" MinHeight="500" MinWidth="1100"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:vm="clr-namespace:PixiEditor.ViewModels"
-        xmlns:vws="clr-namespace:PixiEditor.Views"
-        xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
-        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
-        xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
-        xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
-        xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
-        xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
-        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock"
-        mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
-        Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
-        WindowStartupLocation="CenterScreen" WindowState="Maximized">
-    <WindowChrome.WindowChrome>
-        <WindowChrome CaptionHeight="32"
-                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
-    </WindowChrome.WindowChrome>
-
-    <Window.Resources>
-        <ResourceDictionary>
-            <!--<vm:ViewModelMain x:Key="ViewModelMain" />-->
-            <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
-            <converters:BoolToIntConverter x:Key="BoolToIntConverter" />
-            <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter" />
-            <converters:FloatNormalizeConverter x:Key="FloatNormalizeConverter" />
-            <converters:DoubleToIntConverter x:Key="DoubleToIntConverter"/>
-            <ResourceDictionary.MergedDictionaries>
-                <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
-            </ResourceDictionary.MergedDictionaries>
-        </ResourceDictionary>
-    </Window.Resources>
-
-    <Window.CommandBindings>
-        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
-                        Executed="CommandBinding_Executed_Close" />
-        <CommandBinding Command="{x:Static SystemCommands.MaximizeWindowCommand}"
-                        CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Maximize" />
-        <CommandBinding Command="{x:Static SystemCommands.MinimizeWindowCommand}"
-                        CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Minimize" />
-        <CommandBinding Command="{x:Static SystemCommands.RestoreWindowCommand}" CanExecute="CommandBinding_CanExecute"
-                        Executed="CommandBinding_Executed_Restore" />
-    </Window.CommandBindings>
-
-    <i:Interaction.Triggers>
-        <i:EventTrigger EventName="KeyDown">
-            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyDownCommand}" PassEventArgsToCommand="True" />
-        </i:EventTrigger>
-        <i:EventTrigger EventName="KeyUp">
-            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyUpCommand}" PassEventArgsToCommand="True"/>
-        </i:EventTrigger>
-        <i:EventTrigger EventName="ContentRendered">
-            <i:InvokeCommandAction Command="{Binding OnStartupCommand}" />
-        </i:EventTrigger>
-        <i:EventTrigger EventName="Closing">
-            <cmd:EventToCommand Command="{Binding CloseWindowCommand}" PassEventArgsToCommand="True" />
-        </i:EventTrigger>
-    </i:Interaction.Triggers>
-    <Grid Name="mainGrid" Margin="5" Focusable="True">
-        <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="45" />
-            <ColumnDefinition Width="1*" />
-        </Grid.ColumnDefinitions>
-        <Grid.RowDefinitions>
-            <RowDefinition Height="30" />
-            <RowDefinition Height="40" />
-            <RowDefinition Height="1*" />
-            <RowDefinition Height="30" />
-        </Grid.RowDefinitions>
-        <i:Interaction.Behaviors>
-            <behaviours:ClearFocusOnClickBehavior/>
-        </i:Interaction.Behaviors>
-        <DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Background="{StaticResource MainColor}">
-            <Image DockPanel.Dock="Left" HorizontalAlignment="Left" VerticalAlignment="Top"
-                   Source="/Images/PixiEditorLogo.png" Width="20" Height="20" Margin="5,5,0,0" />
-            <Menu WindowChrome.IsHitTestVisibleInChrome="True" Margin="10, 4, 0, 0" DockPanel.Dock="Left"
-                  HorizontalAlignment="Left" VerticalAlignment="Top" Background="Transparent" IsMainMenu="True">
-                <Menu.Resources>
-                    <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource menuItemStyle}" />
-                </Menu.Resources>
-                <MenuItem Header="_File">
-                    <MenuItem InputGestureText="CTRL+N" Header="_New" Command="{Binding FileSubViewModel.OpenNewFilePopupCommand}" />
-                    <MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="{Binding FileSubViewModel.OpenFileCommand}" />
-                    <MenuItem Header="_Recent" ItemsSource="{Binding FileSubViewModel.RecentlyOpened}" x:Name="recentItemMenu" IsEnabled="{Binding FileSubViewModel.HasRecent}">
-                        <MenuItem.ItemContainerStyle>
-                            <Style TargetType="MenuItem" BasedOn="{StaticResource menuItemStyle}">
-                                <Setter Property="Command" Value="{Binding ElementName=recentItemMenu, Path=DataContext.FileSubViewModel.OpenRecentCommand}"/>
-                                <Setter Property="CommandParameter" Value="{Binding}"/>
-                            </Style>
-                        </MenuItem.ItemContainerStyle>
-                    </MenuItem>
-                    <MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="{Binding FileSubViewModel.SaveDocumentCommand}" />
-                    <MenuItem Header="_Save As..." InputGestureText="Ctrl+Shift+S"
-                              Command="{Binding FileSubViewModel.SaveDocumentCommand}" CommandParameter="AsNew" />
-                    <MenuItem Header="_Export" InputGestureText="Ctrl+Shift+Alt+S" Command="{Binding FileSubViewModel.ExportFileCommand}" />
-                    <Separator />
-                    <MenuItem Header="_Exit" Command="{x:Static SystemCommands.CloseWindowCommand}" />
-                </MenuItem>
-                <MenuItem Header="_Edit">
-                    <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Command="{Binding UndoSubViewModel.UndoCommand}" />
-                    <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Command="{Binding UndoSubViewModel.RedoCommand}" />
-                    <Separator />
-                    <MenuItem Header="_Cut" Command="{Binding ClipboardSubViewModel.CutCommand}" InputGestureText="Ctrl+X" />
-                    <MenuItem Header="_Copy" Command="{Binding ClipboardSubViewModel.CopyCommand}" InputGestureText="Ctrl+C" />
-                    <MenuItem Header="_Paste" Command="{Binding ClipboardSubViewModel.PasteCommand}" InputGestureText="Ctrl+V" />
-                    <MenuItem Header="_Duplicate" Command="{Binding ClipboardSubViewModel.DuplicateCommand}" InputGestureText="Ctrl+J" />
-                    <Separator />
-                    <MenuItem Header="_Delete Selected" Command="{Binding DocumentSubViewModel.DeletePixelsCommand}"
-                              InputGestureText="Delete" />
-                    <Separator />
-                    <MenuItem Header="_Settings" Command="{Binding MiscSubViewModel.OpenSettingsWindowCommand}" />
-                </MenuItem>
-                <MenuItem Header="_Select">
-                    <MenuItem Header="_Select All" Command="{Binding SelectionSubViewModel.SelectAllCommand}" InputGestureText="Ctrl+A" />
-                    <MenuItem Header="_Deselect" Command="{Binding SelectionSubViewModel.DeselectCommand}" InputGestureText="Ctrl+D" />
-                </MenuItem>
-                <MenuItem Header="_Document">
-                    <MenuItem Header="Resize Document..." Command="{Binding DocumentSubViewModel.OpenResizePopupCommand}"
-                              InputGestureText="Ctrl+Shift+I" />
-                    <MenuItem Header="Resize Canvas..." Command="{Binding DocumentSubViewModel.OpenResizePopupCommand}"
-                              CommandParameter="canvas" InputGestureText="Ctrl+Shift+C" />
-                    <MenuItem Header="Clip Canvas" Command="{Binding DocumentSubViewModel.ClipCanvasCommand}" />
-                    <Separator/>
-                    <MenuItem Header="Center Content" Command="{Binding DocumentSubViewModel.CenterContentCommand}" />
-                </MenuItem>
-                <MenuItem Header="_View">
-                    <MenuItem Header="Show Grid Lines" IsChecked="{Binding ViewportSubViewModel.GridLinesEnabled, Mode=TwoWay}"
-                              IsCheckable="True" InputGestureText="Ctrl+`"/>
-                </MenuItem>
-                <MenuItem Header="_Help">
-                    <MenuItem Header="Documentation" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki"/>
-                    <MenuItem Header="Repository" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/PixiEditor/PixiEditor"/>
-                    <MenuItem Header="Shortcuts" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki/Shortcuts"/>
-                    <Separator/>
-                    <MenuItem Header="License" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/PixiEditor/PixiEditor/blob/master/LICENSE"/>
-                    <MenuItem Header="Third Party Licenses" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
-                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki/Third-party-licenses"/>
-                </MenuItem>
-                <MenuItem Header="_Debug" Visibility="{Binding IsDebug, Converter={StaticResource BoolToVisibilityConverter}}">
-                    <MenuItem Header="Such empty here..." IsEnabled="False"/>
-                </MenuItem>
-            </Menu>
-            <StackPanel DockPanel.Dock="Right" VerticalAlignment="Top" Orientation="Horizontal"
-                        HorizontalAlignment="Right" WindowChrome.IsHitTestVisibleInChrome="True">
-                <Button Style="{StaticResource MinimizeButtonStyle}" WindowChrome.IsHitTestVisibleInChrome="True"
-                        ToolTip="Minimize"
-                        Command="{x:Static SystemCommands.MinimizeWindowCommand}" />
-                <Button x:Name="RestoreButton" Visibility="Visible" Style="{StaticResource RestoreButtonStyle}"
-                        Command="{x:Static SystemCommands.RestoreWindowCommand}"
-                        WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Restore" />
-                <Button x:Name="MaximizeButton" Visibility="Collapsed" Style="{StaticResource MaximizeButtonStyle}"
-                        Command="{x:Static SystemCommands.MaximizeWindowCommand}"
-                        WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Maximize" />
-                <Button Style="{StaticResource CloseButtonStyle}" WindowChrome.IsHitTestVisibleInChrome="True"
-                        ToolTip="Close"
-                        Command="{x:Static SystemCommands.CloseWindowCommand}" />
-            </StackPanel>
-        </DockPanel>
-        <StackPanel Background="{StaticResource MainColor}" Orientation="Horizontal" Grid.ColumnSpan="3" Grid.Column="0"
-                     Grid.Row="1">
-            <Label Style="{StaticResource BaseLabel}" Margin="10,0,0,0" FontSize="12" VerticalAlignment="Center" Content="{Binding BitmapManager.SelectedTool.ToolName}"/>
-            <Label Style="{StaticResource BaseLabel}" Padding="0" FontSize="12" VerticalAlignment="Center" Content="tool"/>
-            <ItemsControl ItemsSource="{Binding BitmapManager.SelectedTool.Toolbar.Settings}">
-                <ItemsControl.ItemsPanel>
-                    <ItemsPanelTemplate>
-                        <StackPanel Orientation="Horizontal" Margin="10, 0, 0, 0" />
-                    </ItemsPanelTemplate>
-                </ItemsControl.ItemsPanel>
-                <ItemsControl.ItemTemplate>
-                    <DataTemplate>
-                        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,10,0">
-                            <Label
-                                Visibility="{Binding HasLabel, Converter={StaticResource BoolToVisibilityConverter}}"
-                                Foreground="White" Content="{Binding Label}" />
-                            <ContentControl Content="{Binding SettingControl}" />
-                        </StackPanel>
-                    </DataTemplate>
-                </ItemsControl.ItemTemplate>
-            </ItemsControl>
-        </StackPanel>
-        <Grid Grid.Column="1" Grid.Row="2" Background="#303030">
-            <Grid>
-                <DockingManager ActiveContent="{Binding BitmapManager.ActiveDocument, Mode=TwoWay}" 
-                                           DocumentsSource="{Binding BitmapManager.Documents}">
-                    <DockingManager.Theme>
-                        <avalonDockTheme:PixiEditorDockTheme />
-                    </DockingManager.Theme>
-                    <avalondock:DockingManager.LayoutItemContainerStyleSelector>
-                        <ui:PanelsStyleSelector>
-                            <ui:PanelsStyleSelector.DocumentTabStyle>
-                                <Style TargetType="{x:Type avalondock:LayoutItem}">
-                                    <Setter Property="Title" Value="{Binding Model.Name}" />
-                                    <Setter Property="CloseCommand" Value="{Binding Model.RequestCloseDocumentCommand}" />
-                                </Style>
-                            </ui:PanelsStyleSelector.DocumentTabStyle>
-                        </ui:PanelsStyleSelector>
-                    </avalondock:DockingManager.LayoutItemContainerStyleSelector>
-                    <DockingManager.LayoutItemTemplateSelector>
-                        <ui:DocumentsTemplateSelector>
-                            <ui:DocumentsTemplateSelector.DocumentsViewTemplate>
-                                <DataTemplate DataType="{x:Type vm:ViewModelMain}">
-                                    <usercontrols:DrawingViewPort
-                                        ZoomPercentage="{Binding ZoomPercentage}"
-                                        RecenterZoombox="{Binding RecenterZoombox}"
-                                        GridLinesVisible="{Binding XamlAccesibleViewModel.ViewportSubViewModel.GridLinesEnabled}"
-                                        Cursor="{Binding XamlAccesibleViewModel.ToolsSubViewModel.ToolCursor}"
-                                        MiddleMouseClickedCommand="{Binding XamlAccesibleViewModel.ToolsSubViewModel.SelectToolCommand}"
-                                        ViewportPosition="{Binding ViewportPosition}"
-                                        MouseMoveCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseMoveCommand}"
-                                        MouseDownCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseDownCommand}"
-                                        MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
-                                        MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}">
-                                        <i:Interaction.Triggers>
-                                            <i:EventTrigger EventName="PreviewMouseDown">
-                                                <i:InvokeCommandAction Command="{Binding SetAsActiveOnClickCommand}"/>
-                                            </i:EventTrigger>
-                                        </i:Interaction.Triggers>
-                                    </usercontrols:DrawingViewPort>
-                                </DataTemplate>
-                            </ui:DocumentsTemplateSelector.DocumentsViewTemplate>
-                        </ui:DocumentsTemplateSelector>
-                    </DockingManager.LayoutItemTemplateSelector>
-                    <avalondock:LayoutRoot x:Name="LayoutRoot">
-                        <LayoutPanel Orientation="Horizontal">
-                            <LayoutDocumentPane/>
-                            <LayoutAnchorablePaneGroup Orientation="Vertical" DockWidth="290">
-                                <LayoutAnchorablePane>
-                                <LayoutAnchorable ContentId="colorPicker" Title="Color Picker" CanHide="False"
-                                                             CanClose="False" CanAutoHide="False"
-                                                             CanDockAsTabbedDocument="False" CanFloat="True">
-                                    <colorpicker:StandardColorPicker Grid.Row="0" SelectedColor="{Binding ColorsSubViewModel.PrimaryColor, Mode=TwoWay}"
-                                     SecondaryColor="{Binding ColorsSubViewModel.SecondaryColor, Mode=TwoWay}" Style="{StaticResource DefaultColorPickerStyle}" >
-                                        <i:Interaction.Behaviors>
-                                            <behaviours:GlobalShortcutFocusBehavior/>
-                                        </i:Interaction.Behaviors>
-                                    </colorpicker:StandardColorPicker>
-                                </LayoutAnchorable>
-                                <avalondock:LayoutAnchorable ContentId="swatches" Title="Swatches" CanHide="False"
-                                                         CanClose="False" CanAutoHide="False"
-                                                         CanDockAsTabbedDocument="False" CanFloat="True">
-                                    <ScrollViewer HorizontalScrollBarVisibility="Disabled"
-                                              VerticalScrollBarVisibility="Auto">
-                                        <ItemsControl ItemsSource="{Binding BitmapManager.ActiveDocument.Swatches}">
-                                            <ItemsControl.ItemsPanel>
-                                                <ItemsPanelTemplate>
-                                                    <WrapPanel Margin="10,10,0,10" Orientation="Horizontal"
-                                                           VerticalAlignment="Top" HorizontalAlignment="Left" />
-                                                </ItemsPanelTemplate>
-                                            </ItemsControl.ItemsPanel>
-                                            <ItemsControl.ItemTemplate>
-                                                <DataTemplate>
-                                                    <Grid Width="45" Height="45" Margin="0 5 5 5">
-                                                        <Border CornerRadius="5.5" Width="44" Height="44">
-                                                            <Border.Background>
-                                                                <ImageBrush ImageSource="../Images/transparentbg.png"
-                                                                        Stretch="UniformToFill">
-                                                                    <ImageBrush.RelativeTransform>
-                                                                        <ScaleTransform ScaleX="6" ScaleY="6" CenterX="0.5"
-                                                                                    CenterY="0.5" />
-                                                                    </ImageBrush.RelativeTransform>
-                                                                </ImageBrush>
-                                                            </Border.Background>
-                                                        </Border>
-                                                        <Border CornerRadius="5.5" BorderThickness="0 0 0 0.1" BorderBrush="White" Cursor="Hand">
-                                                            <Border.Background>
-                                                                <SolidColorBrush Color="{Binding}" />
-                                                            </Border.Background>
-                                                        </Border>
-                                                        <i:Interaction.Triggers>
-                                                            <i:EventTrigger EventName="MouseDown">
-                                                                <i:InvokeCommandAction
-                                                                Command="{Binding
-                                                                    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ColorsSubViewModel.SelectColorCommand}"
-                                                                CommandParameter="{Binding}" />
-                                                            </i:EventTrigger>
-                                                        </i:Interaction.Triggers>
-                                                        <Grid.ContextMenu>
-                                                            <ContextMenu>
-                                                                <MenuItem Header="Remove" Foreground="White"
-                                                                      Command="{Binding ColorsSubViewModel.RemoveSwatchCommand}"
-                                                                      CommandParameter="{Binding}" />
-                                                            </ContextMenu>
-                                                        </Grid.ContextMenu>
-                                                    </Grid>
-                                                </DataTemplate>
-                                            </ItemsControl.ItemTemplate>
-                                        </ItemsControl>
-                                    </ScrollViewer>
-                                </avalondock:LayoutAnchorable>
-                            </LayoutAnchorablePane>
-                                <LayoutAnchorablePane>
-                                    <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
-                                                         CanClose="False" CanAutoHide="False"
-                                                         CanDockAsTabbedDocument="True" CanFloat="True">
-                                        <Grid>
-                                            <Grid.RowDefinitions>
-                                                <RowDefinition Height="40"/>
-                                                <RowDefinition Height="30"/>
-                                                <RowDefinition Height="15"/>
-                                                <RowDefinition Height="1*"/>
-                                            </Grid.RowDefinitions>
-                                            <Button Grid.Row="0" Command="{Binding LayersSubViewModel.NewLayerCommand}" Height="30" Content="New Layer"
-                                            HorizontalAlignment="Stretch" Margin="5"
-                                            Style="{StaticResource DarkRoundButton}" />
-                                            <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0">
-                                                <Label Content="Opacity" Foreground="White" VerticalAlignment="Center"/>
-                                                <vws:NumberInput 
-                                                    Min="0" Max="100"
-                                                    IsEnabled="{Binding Path=BitmapManager.ActiveDocument, 
-                                                    Converter={StaticResource NotNullToBoolConverter}}" 
-                                                    Width="40" Height="20"
-                                                    VerticalAlignment="Center"
-                                                   Value="{Binding BitmapManager.ActiveDocument.ActiveLayer.OpacityUndoTriggerable, Mode=TwoWay, 
-                                            Converter={StaticResource FloatNormalizeConverter}}" />
-                                                <Label Content="%" Foreground="White" VerticalAlignment="Center"/>
-                                            </StackPanel>
-                                            <Separator Grid.Row="2" Background="{StaticResource BrighterAccentColor}"/>
-                                            <ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
-                                                <ItemsControl ItemsSource="{Binding BitmapManager.ActiveDocument.Layers}"
-                                                      x:Name="layersItemsControl" AlternationCount="9999">
-                                                    <ItemsControl.ItemsPanel>
-                                                        <ItemsPanelTemplate>
-                                                            <ui:ReversedOrderStackPanel Orientation="Vertical" />
-                                                        </ItemsPanelTemplate>
-                                                    </ItemsControl.ItemsPanel>
-                                                    <ItemsControl.ItemTemplate>
-                                                        <DataTemplate>
-                                                            <vws:LayerItem Tag="{Binding DataContext, ElementName=mainWindow}" LayerIndex="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" SetActiveLayerCommand="{Binding Path=DataContext.LayersSubViewModel.SetActiveLayerCommand, ElementName=mainWindow}"
-                                                                   LayerName="{Binding Name, Mode=TwoWay}" IsActive="{Binding IsActive, Mode=TwoWay}"
-                                                                   IsRenaming="{Binding IsRenaming, Mode=TwoWay}"
-                                                                   PreviewImage="{Binding LayerBitmap}"
-                                                                   MoveToBackCommand="{Binding DataContext.LayersSubViewModel.MoveToBackCommand, ElementName=mainWindow}"
-                                                                   MoveToFrontCommand="{Binding DataContext.LayersSubViewModel.MoveToFrontCommand, ElementName=mainWindow}">
-                                                                <vws:LayerItem.ContextMenu>
-                                                                    <ContextMenu>
-                                                                        <MenuItem Header="Delete"
-                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.DeleteLayerCommand,
-                                                                                    RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" />
-                                                                        <MenuItem Header="Rename"
-                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.RenameLayerCommand,
-                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" />
-                                                                        <MenuItem Header="Move to front"
-                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MoveToFrontCommand, 
-                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" />
-                                                                        <MenuItem Header="Move to back"
-                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MoveToBackCommand, 
-                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" />
-                                                                        <MenuItem Header="Merge with above"
-                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MergeWithAboveCommand, 
-                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" />
-                                                                        <MenuItem Header="Merge with below"
-                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MergeWithBelowCommand, 
-                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
-                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
-                                Path=(ItemsControl.AlternationIndex)}" />
-                                                                    </ContextMenu>
-                                                                </vws:LayerItem.ContextMenu>
-                                                            </vws:LayerItem>
-                                                        </DataTemplate>
-                                                    </ItemsControl.ItemTemplate>
-                                                </ItemsControl>
-                                            </ScrollViewer>
-                                        </Grid>
-                                    </LayoutAnchorable>
-                                </LayoutAnchorablePane>
-                            </LayoutAnchorablePaneGroup>
-                        </LayoutPanel>
-                    </avalondock:LayoutRoot>
-                </DockingManager>
-            </Grid>
-        </Grid>
-
-        <StackPanel Orientation="Vertical" Cursor="Arrow" Grid.Row="2" Grid.Column="0"
-                    Background="{StaticResource AccentColor}" Grid.RowSpan="2">
-
-            <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ToolSet}">
-                <ItemsControl.ItemTemplate>
-                    <DataTemplate>
-                        <Button BorderBrush="White"
-                                BorderThickness="{Binding IsActive, Converter={StaticResource BoolToIntConverter}}"
-                                Style="{StaticResource ToolButtonStyle}"
-                                Command="{Binding Path=DataContext.ToolsSubViewModel.SelectToolCommand,
-                            RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
-                                CommandParameter="{Binding}" ToolTip="{Binding Tooltip}">
-                            <Button.Background>
-                                <ImageBrush ImageSource="{Binding ImagePath}" Stretch="Uniform" />
-                            </Button.Background>
-                        </Button>
-                    </DataTemplate>
-                </ItemsControl.ItemTemplate>
-            </ItemsControl>
-        </StackPanel>
-
-        <Grid Grid.Row="3" Grid.Column="1">
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="*"/>
-                <ColumnDefinition Width="290"/>
-            </Grid.ColumnDefinitions>
-            <DockPanel>
-            <TextBlock Text="{Binding BitmapManager.SelectedTool.ActionDisplay}" Foreground="White" FontSize="15" Margin="10,0,0,0" VerticalAlignment="Center"/>
-            <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
-                <TextBlock Text="X:" Foreground="White" FontSize="16"/>
-                <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseXOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
-                <TextBlock Text="Y:" Foreground="White" FontSize="16"/>
-                <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseYOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
-            </StackPanel>
-        </DockPanel>
-        <StackPanel Margin="10,0,0,0" VerticalAlignment="Center" Grid.Row="3"
-                       Grid.Column="3" Orientation="Horizontal">
-            <Button Style="{StaticResource BaseDarkButton}" 
-                    Visibility="{Binding UpdateSubViewModel.UpdateReadyToInstall, Converter={StaticResource BoolToVisibilityConverter}}" FontSize="14" Height="20" 
-                    Command="{Binding UpdateSubViewModel.RestartApplicationCommand}">Restart</Button>
-            <TextBlock VerticalAlignment="Center" Padding="10" HorizontalAlignment="Right"
-                       Foreground="White" FontSize="14"  Text="{Binding UpdateSubViewModel.VersionText}" />
-        </StackPanel>
-        </Grid>
-    </Grid>
+<Window x:Class="PixiEditor.MainWindow" MinHeight="500" MinWidth="1100"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:vm="clr-namespace:PixiEditor.ViewModels"
+        xmlns:vws="clr-namespace:PixiEditor.Views"
+        xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
+        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
+        xmlns:ui="clr-namespace:PixiEditor.Helpers.UI"
+        xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
+        xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
+        xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" xmlns:usercontrols="clr-namespace:PixiEditor.Views.UserControls" xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours" 
+        xmlns:avalonDockTheme="clr-namespace:PixiEditor.Styles.AvalonDock" d:DataContext="{d:DesignInstance Type=vm:ViewModelMain}"
+        mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
+        Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
+        WindowStartupLocation="CenterScreen" WindowState="Maximized">
+    <WindowChrome.WindowChrome>
+        <WindowChrome CaptionHeight="32"
+                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
+    </WindowChrome.WindowChrome>
+
+    <Window.Resources>
+        <ResourceDictionary>
+            <!--<vm:ViewModelMain x:Key="ViewModelMain" />-->
+            <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
+            <converters:BoolToIntConverter x:Key="BoolToIntConverter" />
+            <converters:NotNullToBoolConverter x:Key="NotNullToBoolConverter" />
+            <converters:FloatNormalizeConverter x:Key="FloatNormalizeConverter" />
+            <converters:DoubleToIntConverter x:Key="DoubleToIntConverter"/>
+            <ResourceDictionary.MergedDictionaries>
+                <ResourceDictionary Source="pack://application:,,,/ColorPicker;component/Styles/DefaultColorPickerStyle.xaml" />
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </Window.Resources>
+
+    <Window.CommandBindings>
+        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Close" />
+        <CommandBinding Command="{x:Static SystemCommands.MaximizeWindowCommand}"
+                        CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Maximize" />
+        <CommandBinding Command="{x:Static SystemCommands.MinimizeWindowCommand}"
+                        CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed_Minimize" />
+        <CommandBinding Command="{x:Static SystemCommands.RestoreWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Restore" />
+    </Window.CommandBindings>
+
+    <i:Interaction.Triggers>
+        <i:EventTrigger EventName="KeyDown">
+            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyDownCommand}" PassEventArgsToCommand="True" />
+        </i:EventTrigger>
+        <i:EventTrigger EventName="KeyUp">
+            <cmd:EventToCommand Command="{Binding IoSubViewModel.KeyUpCommand}" PassEventArgsToCommand="True"/>
+        </i:EventTrigger>
+        <i:EventTrigger EventName="ContentRendered">
+            <i:InvokeCommandAction Command="{Binding OnStartupCommand}" />
+        </i:EventTrigger>
+        <i:EventTrigger EventName="Closing">
+            <cmd:EventToCommand Command="{Binding CloseWindowCommand}" PassEventArgsToCommand="True" />
+        </i:EventTrigger>
+    </i:Interaction.Triggers>
+    <Grid Name="mainGrid" Margin="5" Focusable="True">
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="45" />
+            <ColumnDefinition Width="1*" />
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="30" />
+            <RowDefinition Height="40" />
+            <RowDefinition Height="1*" />
+            <RowDefinition Height="30" />
+        </Grid.RowDefinitions>
+        <i:Interaction.Behaviors>
+            <behaviours:ClearFocusOnClickBehavior/>
+        </i:Interaction.Behaviors>
+        <DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Background="{StaticResource MainColor}">
+            <Image DockPanel.Dock="Left" HorizontalAlignment="Left" VerticalAlignment="Top"
+                   Source="/Images/PixiEditorLogo.png" Width="20" Height="20" Margin="5,5,0,0" />
+            <Menu WindowChrome.IsHitTestVisibleInChrome="True" Margin="10, 4, 0, 0" DockPanel.Dock="Left"
+                  HorizontalAlignment="Left" VerticalAlignment="Top" Background="Transparent" IsMainMenu="True">
+                <Menu.Resources>
+                    <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource menuItemStyle}" />
+                </Menu.Resources>
+                <MenuItem Header="_File">
+                    <MenuItem InputGestureText="CTRL+N" Header="_New" Command="{Binding FileSubViewModel.OpenNewFilePopupCommand}" />
+                    <MenuItem Header="_Open" InputGestureText="Ctrl+O" Command="{Binding FileSubViewModel.OpenFileCommand}" />
+                    <MenuItem Header="_Recent" ItemsSource="{Binding FileSubViewModel.RecentlyOpened}" x:Name="recentItemMenu" IsEnabled="{Binding FileSubViewModel.HasRecent}">
+                        <MenuItem.ItemContainerStyle>
+                            <Style TargetType="MenuItem" BasedOn="{StaticResource menuItemStyle}">
+                                <Setter Property="Command" Value="{Binding ElementName=recentItemMenu, Path=DataContext.FileSubViewModel.OpenRecentCommand}"/>
+                                <Setter Property="CommandParameter" Value="{Binding}"/>
+                            </Style>
+                        </MenuItem.ItemContainerStyle>
+                    </MenuItem>
+                    <MenuItem Header="_Save" InputGestureText="Ctrl+S" Command="{Binding FileSubViewModel.SaveDocumentCommand}" />
+                    <MenuItem Header="_Save As..." InputGestureText="Ctrl+Shift+S"
+                              Command="{Binding FileSubViewModel.SaveDocumentCommand}" CommandParameter="AsNew" />
+                    <MenuItem Header="_Export" InputGestureText="Ctrl+Shift+Alt+S" Command="{Binding FileSubViewModel.ExportFileCommand}" />
+                    <Separator />
+                    <MenuItem Header="_Exit" Command="{x:Static SystemCommands.CloseWindowCommand}" />
+                </MenuItem>
+                <MenuItem Header="_Edit">
+                    <MenuItem Header="_Undo" InputGestureText="Ctrl+Z" Command="{Binding UndoSubViewModel.UndoCommand}" />
+                    <MenuItem Header="_Redo" InputGestureText="Ctrl+Y" Command="{Binding UndoSubViewModel.RedoCommand}" />
+                    <Separator />
+                    <MenuItem Header="_Cut" Command="{Binding ClipboardSubViewModel.CutCommand}" InputGestureText="Ctrl+X" />
+                    <MenuItem Header="_Copy" Command="{Binding ClipboardSubViewModel.CopyCommand}" InputGestureText="Ctrl+C" />
+                    <MenuItem Header="_Paste" Command="{Binding ClipboardSubViewModel.PasteCommand}" InputGestureText="Ctrl+V" />
+                    <MenuItem Header="_Duplicate" Command="{Binding ClipboardSubViewModel.DuplicateCommand}" InputGestureText="Ctrl+J" />
+                    <Separator />
+                    <MenuItem Header="_Delete Selected" Command="{Binding DocumentSubViewModel.DeletePixelsCommand}"
+                              InputGestureText="Delete" />
+                    <Separator />
+                    <MenuItem Header="_Settings" Command="{Binding MiscSubViewModel.OpenSettingsWindowCommand}" />
+                </MenuItem>
+                <MenuItem Header="_Select">
+                    <MenuItem Header="_Select All" Command="{Binding SelectionSubViewModel.SelectAllCommand}" InputGestureText="Ctrl+A" />
+                    <MenuItem Header="_Deselect" Command="{Binding SelectionSubViewModel.DeselectCommand}" InputGestureText="Ctrl+D" />
+                </MenuItem>
+                <MenuItem Header="_Document">
+                    <MenuItem Header="_Resize Document..." Command="{Binding DocumentSubViewModel.OpenResizePopupCommand}"
+                              InputGestureText="Ctrl+Shift+I" />
+                    <MenuItem Header="_Resize Canvas..." Command="{Binding DocumentSubViewModel.OpenResizePopupCommand}"
+                              CommandParameter="canvas" InputGestureText="Ctrl+Shift+C" />
+                    <MenuItem Header="_Clip Canvas" Command="{Binding DocumentSubViewModel.ClipCanvasCommand}" />
+                    <Separator/>
+                    <MenuItem Header="_Center Content" Command="{Binding DocumentSubViewModel.CenterContentCommand}" />
+                </MenuItem>
+                <MenuItem Header="_View">
+                    <MenuItem Header="_Show Grid Lines" IsChecked="{Binding ViewportSubViewModel.GridLinesEnabled, Mode=TwoWay}"
+                              IsCheckable="True" InputGestureText="Ctrl+`"/>
+                </MenuItem>
+                <MenuItem Header="_Help">
+                    <MenuItem Header="_Documentation" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki"/>
+                    <MenuItem Header="_Repository" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor"/>
+                    <MenuItem Header="_Shortcuts" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki/Shortcuts"/>
+                    <Separator/>
+                    <MenuItem Header="_License" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/blob/master/LICENSE"/>
+                    <MenuItem Header="_Third Party Licenses" Command="{Binding MiscSubViewModel.OpenHyperlinkCommand}"
+                              CommandParameter="https://github.com/PixiEditor/PixiEditor/wiki/Third-party-licenses"/>
+                </MenuItem>
+            </Menu>
+            <StackPanel DockPanel.Dock="Right" VerticalAlignment="Top" Orientation="Horizontal"
+                        HorizontalAlignment="Right" WindowChrome.IsHitTestVisibleInChrome="True">
+                <Button Style="{StaticResource MinimizeButtonStyle}" WindowChrome.IsHitTestVisibleInChrome="True"
+                        ToolTip="Minimize"
+                        Command="{x:Static SystemCommands.MinimizeWindowCommand}" />
+                <Button x:Name="RestoreButton" Visibility="Visible" Style="{StaticResource RestoreButtonStyle}"
+                        Command="{x:Static SystemCommands.RestoreWindowCommand}"
+                        WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Restore" />
+                <Button x:Name="MaximizeButton" Visibility="Collapsed" Style="{StaticResource MaximizeButtonStyle}"
+                        Command="{x:Static SystemCommands.MaximizeWindowCommand}"
+                        WindowChrome.IsHitTestVisibleInChrome="True" ToolTip="Maximize" />
+                <Button Style="{StaticResource CloseButtonStyle}" WindowChrome.IsHitTestVisibleInChrome="True"
+                        ToolTip="Close"
+                        Command="{x:Static SystemCommands.CloseWindowCommand}" />
+            </StackPanel>
+        </DockPanel>
+        <StackPanel Background="{StaticResource MainColor}" Orientation="Horizontal" Grid.ColumnSpan="3" Grid.Column="0"
+                     Grid.Row="1">
+            <Label Style="{StaticResource BaseLabel}" Margin="10,0,0,0" FontSize="12" VerticalAlignment="Center" Content="{Binding BitmapManager.SelectedTool.ToolName}"/>
+            <Label Style="{StaticResource BaseLabel}" Padding="0" FontSize="12" VerticalAlignment="Center" Content="tool"/>
+            <ItemsControl ItemsSource="{Binding BitmapManager.SelectedTool.Toolbar.Settings}">
+                <ItemsControl.ItemsPanel>
+                    <ItemsPanelTemplate>
+                        <StackPanel Orientation="Horizontal" Margin="10, 0, 0, 0" />
+                    </ItemsPanelTemplate>
+                </ItemsControl.ItemsPanel>
+                <ItemsControl.ItemTemplate>
+                    <DataTemplate>
+                        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,10,0">
+                            <Label
+                                Visibility="{Binding HasLabel, Converter={StaticResource BoolToVisibilityConverter}}"
+                                Foreground="White" Content="{Binding Label}" />
+                            <ContentControl Content="{Binding SettingControl}" />
+                        </StackPanel>
+                    </DataTemplate>
+                </ItemsControl.ItemTemplate>
+            </ItemsControl>
+        </StackPanel>
+        <Grid Grid.Column="1" Grid.Row="2" Background="#303030">
+            <Grid>
+                <DockingManager ActiveContent="{Binding BitmapManager.ActiveDocument, Mode=TwoWay}" 
+                                           DocumentsSource="{Binding BitmapManager.Documents}">
+                    <DockingManager.Theme>
+                        <avalonDockTheme:PixiEditorDockTheme />
+                    </DockingManager.Theme>
+                    <avalondock:DockingManager.LayoutItemContainerStyleSelector>
+                        <ui:PanelsStyleSelector>
+                            <ui:PanelsStyleSelector.DocumentTabStyle>
+                                <Style TargetType="{x:Type avalondock:LayoutItem}">
+                                    <Setter Property="Title" Value="{Binding Model.Name}" />
+                                    <Setter Property="CloseCommand" Value="{Binding Model.RequestCloseDocumentCommand}" />
+                                </Style>
+                            </ui:PanelsStyleSelector.DocumentTabStyle>
+                        </ui:PanelsStyleSelector>
+                    </avalondock:DockingManager.LayoutItemContainerStyleSelector>
+                    <DockingManager.LayoutItemTemplateSelector>
+                        <ui:DocumentsTemplateSelector>
+                            <ui:DocumentsTemplateSelector.DocumentsViewTemplate>
+                                <DataTemplate DataType="{x:Type vm:ViewModelMain}">
+                                    <usercontrols:DrawingViewPort
+                                        ZoomPercentage="{Binding ZoomPercentage}"
+                                        RecenterZoombox="{Binding RecenterZoombox}"
+                                        GridLinesVisible="{Binding XamlAccesibleViewModel.ViewportSubViewModel.GridLinesEnabled}"
+                                        Cursor="{Binding XamlAccesibleViewModel.ToolsSubViewModel.ToolCursor}"
+                                        MiddleMouseClickedCommand="{Binding XamlAccesibleViewModel.ToolsSubViewModel.SelectToolCommand}"
+                                        ViewportPosition="{Binding ViewportPosition}"
+                                        MouseMoveCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseMoveCommand}"
+                                        MouseDownCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseDownCommand}"
+                                        MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
+                                        MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}">
+                                        <i:Interaction.Triggers>
+                                            <i:EventTrigger EventName="PreviewMouseDown">
+                                                <i:InvokeCommandAction Command="{Binding SetAsActiveOnClickCommand}"/>
+                                            </i:EventTrigger>
+                                        </i:Interaction.Triggers>
+                                    </usercontrols:DrawingViewPort>
+                                </DataTemplate>
+                            </ui:DocumentsTemplateSelector.DocumentsViewTemplate>
+                        </ui:DocumentsTemplateSelector>
+                    </DockingManager.LayoutItemTemplateSelector>
+                    <avalondock:LayoutRoot x:Name="LayoutRoot">
+                        <LayoutPanel Orientation="Horizontal">
+                            <LayoutDocumentPane/>
+                            <LayoutAnchorablePaneGroup Orientation="Vertical" DockWidth="290">
+                                <LayoutAnchorablePane>
+                                <LayoutAnchorable ContentId="colorPicker" Title="Color Picker" CanHide="False"
+                                                             CanClose="False" CanAutoHide="False"
+                                                             CanDockAsTabbedDocument="False" CanFloat="True">
+                                    <colorpicker:StandardColorPicker Grid.Row="0" SelectedColor="{Binding ColorsSubViewModel.PrimaryColor, Mode=TwoWay}"
+                                     SecondaryColor="{Binding ColorsSubViewModel.SecondaryColor, Mode=TwoWay}" Style="{StaticResource DefaultColorPickerStyle}" >
+                                        <i:Interaction.Behaviors>
+                                            <behaviours:GlobalShortcutFocusBehavior/>
+                                        </i:Interaction.Behaviors>
+                                    </colorpicker:StandardColorPicker>
+                                </LayoutAnchorable>
+                                <avalondock:LayoutAnchorable ContentId="swatches" Title="Swatches" CanHide="False"
+                                                         CanClose="False" CanAutoHide="False"
+                                                         CanDockAsTabbedDocument="False" CanFloat="True">
+                                    <ScrollViewer HorizontalScrollBarVisibility="Disabled"
+                                              VerticalScrollBarVisibility="Auto">
+                                        <ItemsControl ItemsSource="{Binding BitmapManager.ActiveDocument.Swatches}">
+                                            <ItemsControl.ItemsPanel>
+                                                <ItemsPanelTemplate>
+                                                    <WrapPanel Margin="10,10,0,10" Orientation="Horizontal"
+                                                           VerticalAlignment="Top" HorizontalAlignment="Left" />
+                                                </ItemsPanelTemplate>
+                                            </ItemsControl.ItemsPanel>
+                                            <ItemsControl.ItemTemplate>
+                                                <DataTemplate>
+                                                    <Grid Width="45" Height="45" Margin="0 5 5 5">
+                                                        <Border CornerRadius="5.5" Width="44" Height="44">
+                                                            <Border.Background>
+                                                                <ImageBrush ImageSource="../Images/transparentbg.png"
+                                                                        Stretch="UniformToFill">
+                                                                    <ImageBrush.RelativeTransform>
+                                                                        <ScaleTransform ScaleX="6" ScaleY="6" CenterX="0.5"
+                                                                                    CenterY="0.5" />
+                                                                    </ImageBrush.RelativeTransform>
+                                                                </ImageBrush>
+                                                            </Border.Background>
+                                                        </Border>
+                                                        <Border CornerRadius="5.5" BorderThickness="0 0 0 0.1" BorderBrush="White" Cursor="Hand">
+                                                            <Border.Background>
+                                                                <SolidColorBrush Color="{Binding}" />
+                                                            </Border.Background>
+                                                        </Border>
+                                                        <i:Interaction.Triggers>
+                                                            <i:EventTrigger EventName="MouseDown">
+                                                                <i:InvokeCommandAction
+                                                                Command="{Binding
+                                                                    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ColorsSubViewModel.SelectColorCommand}"
+                                                                CommandParameter="{Binding}" />
+                                                            </i:EventTrigger>
+                                                        </i:Interaction.Triggers>
+                                                        <Grid.ContextMenu>
+                                                            <ContextMenu>
+                                                                <MenuItem Header="Remove" Foreground="White"
+                                                                      Command="{Binding ColorsSubViewModel.RemoveSwatchCommand}"
+                                                                      CommandParameter="{Binding}" />
+                                                            </ContextMenu>
+                                                        </Grid.ContextMenu>
+                                                    </Grid>
+                                                </DataTemplate>
+                                            </ItemsControl.ItemTemplate>
+                                        </ItemsControl>
+                                    </ScrollViewer>
+                                </avalondock:LayoutAnchorable>
+                            </LayoutAnchorablePane>
+                                <LayoutAnchorablePane>
+                                    <LayoutAnchorable ContentId="layers" Title="Layers" CanHide="False"
+                                                         CanClose="False" CanAutoHide="False"
+                                                         CanDockAsTabbedDocument="True" CanFloat="True">
+                                        <Grid>
+                                            <Grid.RowDefinitions>
+                                                <RowDefinition Height="40"/>
+                                                <RowDefinition Height="30"/>
+                                                <RowDefinition Height="15"/>
+                                                <RowDefinition Height="1*"/>
+                                            </Grid.RowDefinitions>
+                                            <Button Grid.Row="0" Command="{Binding LayersSubViewModel.NewLayerCommand}" Height="30" Content="New Layer"
+                                            HorizontalAlignment="Stretch" Margin="5"
+                                            Style="{StaticResource DarkRoundButton}" />
+                                            <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0">
+                                                <Label Content="Opacity" Foreground="White" VerticalAlignment="Center"/>
+                                                <vws:NumberInput 
+                                                    Min="0" Max="100"
+                                                    IsEnabled="{Binding Path=BitmapManager.ActiveDocument, 
+                                                    Converter={StaticResource NotNullToBoolConverter}}" 
+                                                    Width="40" Height="20"
+                                                    VerticalAlignment="Center"
+                                                   Value="{Binding BitmapManager.ActiveDocument.ActiveLayer.OpacityUndoTriggerable, Mode=TwoWay, 
+                                            Converter={StaticResource FloatNormalizeConverter}}" />
+                                                <Label Content="%" Foreground="White" VerticalAlignment="Center"/>
+                                            </StackPanel>
+                                            <Separator Grid.Row="2" Background="{StaticResource BrighterAccentColor}"/>
+                                            <ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
+                                                <ItemsControl ItemsSource="{Binding BitmapManager.ActiveDocument.Layers}"
+                                                      x:Name="layersItemsControl" AlternationCount="9999">
+                                                    <ItemsControl.ItemsPanel>
+                                                        <ItemsPanelTemplate>
+                                                            <ui:ReversedOrderStackPanel Orientation="Vertical" />
+                                                        </ItemsPanelTemplate>
+                                                    </ItemsControl.ItemsPanel>
+                                                    <ItemsControl.ItemTemplate>
+                                                        <DataTemplate>
+                                                            <vws:LayerItem Tag="{Binding DataContext, ElementName=mainWindow}" LayerIndex="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" SetActiveLayerCommand="{Binding Path=DataContext.LayersSubViewModel.SetActiveLayerCommand, ElementName=mainWindow}"
+                                                                   LayerName="{Binding Name, Mode=TwoWay}" IsActive="{Binding IsActive, Mode=TwoWay}"
+                                                                   IsRenaming="{Binding IsRenaming, Mode=TwoWay}"
+                                                                   PreviewImage="{Binding LayerBitmap}" 
+                                                                   LayerColor="{Binding LayerHighlightColor}"
+                                                                   MoveToBackCommand="{Binding DataContext.LayersSubViewModel.MoveToBackCommand, ElementName=mainWindow}"
+                                                                   MoveToFrontCommand="{Binding DataContext.LayersSubViewModel.MoveToFrontCommand, ElementName=mainWindow}">
+                                                                <vws:LayerItem.ContextMenu>
+                                                                    <ContextMenu>
+                                                                        <MenuItem Header="Delete"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.DeleteLayersCommand,
+                                                                                    RelativeSource={RelativeSource AncestorType=ContextMenu}}"
+
+                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" />
+                                                                        <MenuItem Header="Rename"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.RenameLayerCommand,
+                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
+                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" />
+                                                                        <MenuItem Header="Move to front"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MoveToFrontCommand, 
+                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
+                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" />
+                                                                        <MenuItem Header="Move to back"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MoveToBackCommand, 
+                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
+                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" />
+                                                                        <Separator/>
+                                                                        <MenuItem Header="Merge selected"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MergeSelectedCommand, 
+                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
+                                                                        <MenuItem Header="Merge with above"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MergeWithAboveCommand, 
+                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
+                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" />
+                                                                        <MenuItem Header="Merge with below"
+                                                                                  Command="{Binding PlacementTarget.Tag.LayersSubViewModel.MergeWithBelowCommand, 
+                                                                            RelativeSource={RelativeSource AncestorType=ContextMenu}}"
+                                                                                  CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},
+                                Path=(ItemsControl.AlternationIndex)}" />
+                                                                    </ContextMenu>
+                                                                </vws:LayerItem.ContextMenu>
+                                                            </vws:LayerItem>
+                                                        </DataTemplate>
+                                                    </ItemsControl.ItemTemplate>
+                                                </ItemsControl>
+                                            </ScrollViewer>
+                                        </Grid>
+                                    </LayoutAnchorable>
+                                </LayoutAnchorablePane>
+                            </LayoutAnchorablePaneGroup>
+                        </LayoutPanel>
+                    </avalondock:LayoutRoot>
+                </DockingManager>
+            </Grid>
+        </Grid>
+
+        <StackPanel Orientation="Vertical" Cursor="Arrow" Grid.Row="2" Grid.Column="0"
+                    Background="{StaticResource AccentColor}" Grid.RowSpan="2">
+
+            <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ToolSet}">
+                <ItemsControl.ItemTemplate>
+                    <DataTemplate>
+                        <Button BorderBrush="White"
+                                BorderThickness="{Binding IsActive, Converter={StaticResource BoolToIntConverter}}"
+                                Style="{StaticResource ToolButtonStyle}"
+                                Command="{Binding Path=DataContext.ToolsSubViewModel.SelectToolCommand,
+                            RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
+                                CommandParameter="{Binding}" ToolTip="{Binding Tooltip}">
+                            <Button.Background>
+                                <ImageBrush ImageSource="{Binding ImagePath}" Stretch="Uniform" />
+                            </Button.Background>
+                        </Button>
+                    </DataTemplate>
+                </ItemsControl.ItemTemplate>
+            </ItemsControl>
+        </StackPanel>
+
+        <Grid Grid.Row="3" Grid.Column="1">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="*"/>
+                <ColumnDefinition Width="290"/>
+            </Grid.ColumnDefinitions>
+            <DockPanel>
+            <TextBlock Text="{Binding BitmapManager.SelectedTool.ActionDisplay}" Foreground="White" FontSize="15" Margin="10,0,0,0" VerticalAlignment="Center"/>
+            <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
+                <TextBlock Text="X:" Foreground="White" FontSize="16"/>
+                <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseXOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
+                <TextBlock Text="Y:" Foreground="White" FontSize="16"/>
+                <TextBlock Margin="4,0,10,0" Text="{Binding BitmapManager.ActiveDocument.MouseYOnCanvas, Converter={StaticResource DoubleToIntConverter}}" Foreground="White" FontSize="16"/>
+            </StackPanel>
+        </DockPanel>
+        <StackPanel Margin="10,0,0,0" VerticalAlignment="Center" Grid.Row="3"
+                       Grid.Column="3" Orientation="Horizontal">
+            <Button Style="{StaticResource BaseDarkButton}" 
+                    Visibility="{Binding UpdateSubViewModel.UpdateReadyToInstall, Converter={StaticResource BoolToVisibilityConverter}}" FontSize="14" Height="20" 
+                    Command="{Binding UpdateSubViewModel.RestartApplicationCommand}">Restart</Button>
+            <TextBlock VerticalAlignment="Center" Padding="10" HorizontalAlignment="Right"
+                       Foreground="White" FontSize="14"  Text="{Binding UpdateSubViewModel.VersionText}" />
+        </StackPanel>
+        </Grid>
+    </Grid>
 </Window>
 </Window>

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

@@ -39,6 +39,7 @@ namespace PixiEditor
 
 
         protected override void OnClosing(CancelEventArgs e)
         protected override void OnClosing(CancelEventArgs e)
         {
         {
+            ((ViewModelMain)DataContext).CloseWindow(e);
             viewModel.DiscordViewModel.Dispose();
             viewModel.DiscordViewModel.Dispose();
         }
         }
 
 

+ 1 - 4
PixiEditor/Views/UserControls/LayerItem.xaml

@@ -9,11 +9,8 @@
              mc:Ignorable="d" Focusable="True"
              mc:Ignorable="d" Focusable="True"
              d:DesignHeight="60" d:DesignWidth="250" Name="uc"
              d:DesignHeight="60" d:DesignWidth="250" Name="uc"
              MouseLeave="LayerItem_OnMouseLeave" MouseEnter="LayerItem_OnMouseEnter">
              MouseLeave="LayerItem_OnMouseLeave" MouseEnter="LayerItem_OnMouseEnter">
-    <UserControl.Resources>
-        <converters:BoolToColorConverter x:Key="BoolToColorConverter" />
-    </UserControl.Resources>
     <Border BorderThickness="0 0 0 0.5" BorderBrush="Gray" MinWidth="60" Focusable="True"
     <Border BorderThickness="0 0 0 0.5" BorderBrush="Gray" MinWidth="60" Focusable="True"
-            Background="{Binding IsActive, Mode=TwoWay, Converter={StaticResource BoolToColorConverter}}">
+            Background="{Binding LayerColor, ElementName=uc}" >
         <i:Interaction.Behaviors>
         <i:Interaction.Behaviors>
             <behaviors:ClearFocusOnClickBehavior/>
             <behaviors:ClearFocusOnClickBehavior/>
         </i:Interaction.Behaviors>
         </i:Interaction.Behaviors>

+ 10 - 0
PixiEditor/Views/UserControls/LayerItem.xaml.cs

@@ -82,6 +82,16 @@ namespace PixiEditor.Views
         public static readonly DependencyProperty PreviewImageProperty =
         public static readonly DependencyProperty PreviewImageProperty =
             DependencyProperty.Register("PreviewImage", typeof(WriteableBitmap), typeof(LayerItem), new PropertyMetadata(null));
             DependencyProperty.Register("PreviewImage", typeof(WriteableBitmap), typeof(LayerItem), new PropertyMetadata(null));
 
 
+        public string LayerColor
+        {
+            get { return (string)GetValue(LayerColorProperty); }
+            set { SetValue(LayerColorProperty, value); }
+        }
+
+        // Using a DependencyProperty as the backing store for LayerColor.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty LayerColorProperty =
+            DependencyProperty.Register("LayerColor", typeof(string), typeof(LayerItem), new PropertyMetadata("#00000000"));
+
         public Visibility ControlButtonsVisible
         public Visibility ControlButtonsVisible
         {
         {
             get { return (Visibility)GetValue(ControlButtonsVisibleProperty); }
             get { return (Visibility)GetValue(ControlButtonsVisibleProperty); }

+ 154 - 0
PixiEditorTests/ModelsTests/DataHoldersTests/DocumentLayersTests.cs

@@ -0,0 +1,154 @@
+using System;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.ViewModels.SubViewModels.Main;
+using Xunit;
+
+namespace PixiEditorTests.ModelsTests.DataHoldersTests
+{
+    [Collection("Application collection")]
+    public class DocumentLayersTests
+    {
+        [Fact]
+        public void TestThatToggleLayerDoesNotToggleLastLayer()
+        {
+            Document doc = new (5, 5);
+            doc.AddNewLayer("layer");
+            bool isActive = doc.Layers[^1].IsActive;
+            doc.ToggleLayer(0);
+            Assert.False(doc.Layers[^1].IsActive != isActive);
+        }
+
+        [Fact]
+        public void TestThatToggleLayerTogglesLayer()
+        {
+            Document doc = new (5, 5);
+            doc.AddNewLayer("layer");
+            doc.AddNewLayer("layer 1");
+            doc.Layers[0].IsActive = true;
+            doc.Layers[^1].IsActive = true;
+
+            doc.ToggleLayer(0);
+            Assert.False(doc.Layers[0].IsActive);
+            Assert.True(doc.Layers[1].IsActive);
+        }
+
+        [Fact]
+        public void TestThatToggleLayerDoesNothingOnNonExistingIndex()
+        {
+            Document document = new Document(5, 5);
+            document.AddNewLayer("test");
+            document.ToggleLayer(1);
+            document.ToggleLayer(-1);
+            Assert.True(true);
+        }
+
+        [Theory]
+        [InlineData(0, 2)]
+        [InlineData(2, 0)]
+        [InlineData(1, 1)]
+        public void TestThatSelectLayersRangeSelectsRange(int startIndex, int endIndex)
+        {
+            Document document = new Document(5, 5);
+
+            document.AddNewLayer("1");
+            document.AddNewLayer("2");
+            document.AddNewLayer("3");
+
+            document.SetMainActiveLayer(startIndex);
+
+            document.SelectLayersRange(endIndex);
+
+            for (int i = 0; i < document.Layers.Count; i++)
+            {
+                Assert.Equal(
+                    i >= Math.Min(startIndex, endIndex)
+                    && i <= Math.Max(startIndex, endIndex), 
+                    document.Layers[i].IsActive);
+            }
+        }
+
+        [Theory]
+        [InlineData(0)]
+        [InlineData(1)]
+        [InlineData(2)]
+        public void TestThatDeselectAllExceptDeselectsAllExceptLayer(int index)
+        {
+            Document document = new Document(5, 5);
+
+            document.AddNewLayer("1");
+            document.AddNewLayer("2");
+            document.AddNewLayer("3");
+
+            document.SetMainActiveLayer(0);
+            document.Layers[1].IsActive = true;
+            document.Layers[2].IsActive = true;
+
+            document.DeselectAllExcept(document.Layers[index]);
+
+            foreach (var layer in document.Layers)
+            {
+                Assert.Equal(layer == document.Layers[index], layer.IsActive);
+            }
+        }
+
+        [Fact]
+        public void TestThatUpdateLayersColorMakesOnlyOneLayerMainColorAndOtherSecondary()
+        {
+            Document document = new Document(1, 1);
+
+            document.AddNewLayer("1");
+            document.AddNewLayer("2");
+            document.AddNewLayer("3");
+
+            document.SetMainActiveLayer(0);
+            document.Layers[1].IsActive = true; // This makes layer selected, but not main
+            document.Layers[2].IsActive = true;
+
+            document.UpdateLayersColor();
+
+            Assert.Equal(Document.MainSelectedLayerColor, document.Layers[0].LayerHighlightColor);
+            Assert.Equal(Document.SecondarySelectedLayerColor, document.Layers[1].LayerHighlightColor);
+            Assert.Equal(Document.SecondarySelectedLayerColor, document.Layers[2].LayerHighlightColor);
+        }
+
+        [Fact]
+        public void TestThatUpdateLayersColorMakesLayerMainColorAndRestNonActiveReturnsTransparent()
+        {
+            Document document = new Document(1, 1);
+
+            document.AddNewLayer("1");
+            document.AddNewLayer("2");
+            document.AddNewLayer("3");
+
+            document.SetMainActiveLayer(1);
+
+            document.UpdateLayersColor();
+
+            string transparentHex = "#00000000";
+
+            Assert.Equal(transparentHex, document.Layers[0].LayerHighlightColor);
+            Assert.Equal(Document.MainSelectedLayerColor, document.Layers[1].LayerHighlightColor);
+            Assert.Equal(transparentHex, document.Layers[2].LayerHighlightColor);
+        }
+
+        [Fact]
+        public void TestThatSetNextSelectedLayerAsActiveSelectsFirstAvailableLayer()
+        {
+            Document document = new Document(1, 1);
+
+            document.AddNewLayer("1");
+            document.AddNewLayer("2");
+            document.AddNewLayer("3");
+            document.AddNewLayer("4");
+
+            foreach (var layer in document.Layers)
+            {
+                layer.IsActive = true;
+            }
+
+            document.SetNextSelectedLayerAsActive(document.Layers[1].LayerGuid);
+
+            Assert.Equal(document.Layers[0].LayerGuid, document.ActiveLayerGuid);
+        }
+    }
+}

+ 6 - 1
PixiEditorTests/ModelsTests/DataHoldersTests/DocumentTests.cs

@@ -136,6 +136,11 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             manager.ActiveDocument.AddNewLayer("test2");
             manager.ActiveDocument.AddNewLayer("test2");
             manager.ActiveLayer.SetPixel(new Coordinates(1, 1), Colors.Green);
             manager.ActiveLayer.SetPixel(new Coordinates(1, 1), Colors.Green);
 
 
+            foreach (var layer in manager.ActiveDocument.Layers)
+            {
+                layer.IsActive = true;
+            }
+
             doc.CenterContent();
             doc.CenterContent();
 
 
             int midWidth = (int)Math.Floor(docWidth / 2f);
             int midWidth = (int)Math.Floor(docWidth / 2f);
@@ -155,7 +160,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             doc.Layers.Add(new PixiEditor.Models.Layers.Layer("Test"));
             doc.Layers.Add(new PixiEditor.Models.Layers.Layer("Test"));
             doc.Layers.Add(new PixiEditor.Models.Layers.Layer("Test 2"));
             doc.Layers.Add(new PixiEditor.Models.Layers.Layer("Test 2"));
 
 
-            doc.SetActiveLayer(1);
+            doc.SetMainActiveLayer(1);
 
 
             doc.SetNextLayerAsActive(1);
             doc.SetNextLayerAsActive(1);
 
 

+ 1 - 1
PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs

@@ -85,7 +85,7 @@ namespace PixiEditorTests.ViewModelsTests
         {
         {
             ViewModelMain viewModel = new ViewModelMain(Services);
             ViewModelMain viewModel = new ViewModelMain(Services);
 
 
-            Assert.Equal(typeof(MoveTool), viewModel.BitmapManager.SelectedTool.GetType());
+            Assert.Equal(typeof(MoveViewportTool), viewModel.BitmapManager.SelectedTool.GetType());
 
 
             viewModel.ToolsSubViewModel.SelectToolCommand.Execute(new LineTool());
             viewModel.ToolsSubViewModel.SelectToolCommand.Execute(new LineTool());