Browse Source

Fixed almost all warnings - only 18 left

ArtemK123 4 years ago
parent
commit
4b7070c1ae
96 changed files with 3824 additions and 3748 deletions
  1. 10 6
      Custom.ruleset
  2. 1 1
      PixiEditor.UpdateInstaller/App.xaml.cs
  3. 0 0
      PixiEditor/Exceptions/ArrayLengthMismatchException.cs
  4. 7 7
      PixiEditor/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs
  5. 7 7
      PixiEditor/Helpers/Behaviours/HintTextBehavior.cs
  6. 98 91
      PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs
  7. 24 24
      PixiEditor/Helpers/Converters/ToolSizeToIntConverter.cs
  8. 18 16
      PixiEditor/Helpers/Extensions/DictionaryHelper.cs
  9. 128 127
      PixiEditor/Helpers/GlobalMouseHook.cs
  10. 37 49
      PixiEditor/Helpers/RelayCommand.cs
  11. 110 113
      PixiEditor/Models/Colors/ExColor.cs
  12. 18 0
      PixiEditor/Models/Controllers/BitmapChangedEventArgs.cs
  13. 243 256
      PixiEditor/Models/Controllers/BitmapManager.cs
  14. 21 33
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  15. 86 90
      PixiEditor/Models/Controllers/ClipboardController.cs
  16. 18 0
      PixiEditor/Models/Controllers/LayersChangedEventArgs.cs
  17. 7 18
      PixiEditor/Models/Controllers/MouseMovementController.cs
  18. 12 0
      PixiEditor/Models/Controllers/MouseMovementEventArgs.cs
  19. 36 36
      PixiEditor/Models/Controllers/PixelChangesController.cs
  20. 1 2
      PixiEditor/Models/Controllers/Shortcuts/Shortcut.cs
  21. 71 71
      PixiEditor/Models/Controllers/UndoManager.cs
  22. 56 61
      PixiEditor/Models/DataHolders/BitmapPixelChanges.cs
  23. 95 78
      PixiEditor/Models/DataHolders/Change.cs
  24. 284 284
      PixiEditor/Models/DataHolders/Document.cs
  25. 21 0
      PixiEditor/Models/DataHolders/DocumentSizeChangedEventArgs.cs
  26. 92 93
      PixiEditor/Models/IO/Exporter.cs
  27. 2 2
      PixiEditor/Models/IO/Importer.cs
  28. 65 66
      PixiEditor/Models/ImageManipulation/BitmapUtils.cs
  29. 52 51
      PixiEditor/Models/ImageManipulation/Morphology.cs
  30. 3 3
      PixiEditor/Models/ImageManipulation/Transform.cs
  31. 415 422
      PixiEditor/Models/Layers/Layer.cs
  32. 71 71
      PixiEditor/Models/Layers/SerializableLayer.cs
  33. 8 8
      PixiEditor/Models/Position/Coordinates.cs
  34. 15 22
      PixiEditor/Models/Position/CoordinatesCalculator.cs
  35. 13 13
      PixiEditor/Models/Position/DoubleCords.cs
  36. 5 5
      PixiEditor/Models/Position/MousePositionConverter.cs
  37. 10 5
      PixiEditor/Models/Tools/ShapeTool.cs
  38. 42 38
      PixiEditor/Models/Tools/ToolSettings/Settings/FloatSetting.cs
  39. 0 20
      PixiEditor/Models/Tools/ToolSettings/Settings/Setting.cs
  40. 22 0
      PixiEditor/Models/Tools/ToolSettings/Settings/Setting{T}.cs
  41. 1 1
      PixiEditor/Models/Tools/ToolSettings/Toolbars/BasicToolbar.cs
  42. 2 2
      PixiEditor/Models/Tools/ToolSettings/Toolbars/Toolbar.cs
  43. 79 74
      PixiEditor/Models/Tools/Tools/BrightnessTool.cs
  44. 18 18
      PixiEditor/Models/Tools/Tools/CircleTool.cs
  45. 1 2
      PixiEditor/Models/Tools/Tools/ColorPickerTool.cs
  46. 11 10
      PixiEditor/Models/Tools/Tools/LineTool.cs
  47. 17 16
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  48. 21 21
      PixiEditor/Models/Tools/Tools/RectangleTool.cs
  49. 80 80
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  50. 60 60
      PixiEditor/Models/Tools/Tools/ZoomTool.cs
  51. 15 14
      PixiEditor/NotifyableObject.cs
  52. 6 6
      PixiEditor/Properties/AssemblyInfo.cs
  53. 1 1
      PixiEditor/ViewModels/ImportFilePopupViewModel.cs
  54. 1 1
      PixiEditor/ViewModels/SaveFilePopupViewModel.cs
  55. 25 25
      PixiEditor/ViewModels/ViewModelBase.cs
  56. 338 346
      PixiEditor/ViewModels/ViewModelMain.cs
  57. 39 40
      PixiEditor/Views/AnchorPointPicker.xaml.cs
  58. 60 57
      PixiEditor/Views/ConfirmationPopup.xaml.cs
  59. 1 1
      PixiEditor/Views/EditableTextBlock.xaml
  60. 14 17
      PixiEditor/Views/EditableTextBlock.xaml.cs
  61. 1 1
      PixiEditor/Views/ImportFilePopup.xaml.cs
  62. 1 1
      PixiEditor/Views/LayerItem.xaml.cs
  63. 1 1
      PixiEditor/Views/MainDrawingPanel.xaml
  64. 162 165
      PixiEditor/Views/MainDrawingPanel.xaml.cs
  65. 1 1
      PixiEditor/Views/MainWindow.xaml
  66. 2 2
      PixiEditor/Views/MainWindow.xaml.cs
  67. 42 43
      PixiEditor/Views/MenuButton.xaml.cs
  68. 1 1
      PixiEditor/Views/NewFilePopup.xaml.cs
  69. 70 61
      PixiEditor/Views/NumberInput.xaml.cs
  70. 1 1
      PixiEditor/Views/PopupTemplate.xaml.cs
  71. 62 59
      PixiEditor/Views/ResizeCanvasPopup.xaml.cs
  72. 1 1
      PixiEditor/Views/ResizeDocumentPopup.xaml.cs
  73. 66 64
      PixiEditor/Views/Rotatebox.xaml.cs
  74. 1 1
      PixiEditor/Views/SaveFilePopup.xaml.cs
  75. 1 1
      PixiEditor/Views/SizeInput.xaml
  76. 69 71
      PixiEditor/Views/SizeInput.xaml.cs
  77. 1 1
      PixiEditor/Views/SizePicker.xaml.cs
  78. 0 11
      PixiEditorTests/ModelsTests/ControllersTests/BitmapManagerTests.cs
  79. 8 4
      PixiEditorTests/ModelsTests/ControllersTests/ClipboardControllerTests.cs
  80. 21 0
      PixiEditorTests/ModelsTests/ControllersTests/MockedSinglePixelPen.cs
  81. 1 1
      PixiEditorTests/ModelsTests/ControllersTests/MouseMovementControllerTests.cs
  82. 6 3
      PixiEditorTests/ModelsTests/ControllersTests/PixelChangesControllerTests.cs
  83. 1 20
      PixiEditorTests/ModelsTests/ControllersTests/ReadonlyUtilityTests.cs
  84. 80 81
      PixiEditorTests/ModelsTests/ControllersTests/ShortcutControllerTests.cs
  85. 7 0
      PixiEditorTests/ModelsTests/ControllersTests/TestPropertyClass.cs
  86. 23 0
      PixiEditorTests/ModelsTests/ControllersTests/TestReadonlyTool.cs
  87. 7 9
      PixiEditorTests/ModelsTests/ControllersTests/UndoManagerTests.cs
  88. 2 2
      PixiEditorTests/ModelsTests/DataHoldersTests/BitmapPixelChangesTests.cs
  89. 6 2
      PixiEditorTests/ModelsTests/DataHoldersTests/DocumentTests.cs
  90. 2 1
      PixiEditorTests/ModelsTests/DataHoldersTests/SerializableDocumentTests.cs
  91. 0 1
      PixiEditorTests/ModelsTests/IO/ImporterTests.cs
  92. 1 0
      PixiEditorTests/ModelsTests/ImageManipulationTests/BitmapUtilsTests.cs
  93. 130 129
      PixiEditorTests/ModelsTests/LayersTests/LayerTests.cs
  94. 1 0
      PixiEditorTests/ModelsTests/ToolsTests/BrightnessToolTests.cs
  95. 29 27
      PixiEditorTests/ModelsTests/ToolsTests/RectangleToolTests.cs
  96. 2 1
      PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs

+ 10 - 6
Custom.ruleset

@@ -1,8 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <RuleSet Name="Name" Description="Description" ToolsVersion="16.0">
 <RuleSet Name="Name" Description="Description" ToolsVersion="16.0">
-    <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
-        <Rule Id="SA1101" Action="None" />
-        <Rule Id="SA1633" Action="None" />
-        <Rule Id="SA0001" Action="None" />
-    </Rules>
+  <Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
+    <Rule Id="CA1303" Action="None" />
+  </Rules>
+  <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
+    <Rule Id="SA0001" Action="None" />
+    <Rule Id="SA1101" Action="None" />
+    <Rule Id="SA1413" Action="None" />
+    <Rule Id="SA1633" Action="None" />
+  </Rules>
 </RuleSet>
 </RuleSet>

+ 1 - 1
PixiEditor.UpdateInstaller/App.xaml.cs

@@ -3,7 +3,7 @@
 namespace PixiEditor.UpdateInstaller
 namespace PixiEditor.UpdateInstaller
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for App.xaml
+    ///     Interaction logic for App.xaml.
     /// </summary>
     /// </summary>
     public partial class App : Application
     public partial class App : Application
     {
     {

+ 0 - 0
PixiEditor/Exceptions/LengthMismatchException.cs → PixiEditor/Exceptions/ArrayLengthMismatchException.cs


+ 7 - 7
PixiEditor/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs

@@ -42,6 +42,13 @@ namespace PixiEditor.Helpers.Behaviours
             DataObject.AddPastingHandler(AssociatedObject, OnPaste);
             DataObject.AddPastingHandler(AssociatedObject, OnPaste);
         }
         }
 
 
+        protected override void OnDetaching()
+        {
+            base.OnDetaching();
+            AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
+            DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
+        }
+
         private void OnPaste(object sender, DataObjectPastingEventArgs e)
         private void OnPaste(object sender, DataObjectPastingEventArgs e)
         {
         {
             if (e.DataObject.GetDataPresent(DataFormats.Text))
             if (e.DataObject.GetDataPresent(DataFormats.Text))
@@ -64,13 +71,6 @@ namespace PixiEditor.Helpers.Behaviours
             e.Handled = !IsValid(e.Text, false);
             e.Handled = !IsValid(e.Text, false);
         }
         }
 
 
-        protected override void OnDetaching()
-        {
-            base.OnDetaching();
-            AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
-            DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
-        }
-
         private bool IsValid(string newText, bool paste)
         private bool IsValid(string newText, bool paste)
         {
         {
             return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);
             return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);

+ 7 - 7
PixiEditor/Helpers/Behaviours/HintTextBehavior.cs

@@ -32,6 +32,13 @@ namespace PixiEditor.Helpers.Behaviours
             SetHint(true);
             SetHint(true);
         }
         }
 
 
+        protected override void OnDetaching()
+        {
+            base.OnDetaching();
+            AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
+            AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
+        }
+
         private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
         private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
         {
         {
             if (string.IsNullOrEmpty(AssociatedObject.Text))
             if (string.IsNullOrEmpty(AssociatedObject.Text))
@@ -61,12 +68,5 @@ namespace PixiEditor.Helpers.Behaviours
                 AssociatedObject.Foreground = textColor;
                 AssociatedObject.Foreground = textColor;
             }
             }
         }
         }
-
-        protected override void OnDetaching()
-        {
-            base.OnDetaching();
-            AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
-            AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
-        }
     }
     }
 }
 }

+ 98 - 91
PixiEditor/Helpers/Behaviours/TextBoxFocusBehavior.cs

@@ -1,110 +1,117 @@
-using System.Text.RegularExpressions;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Interactivity;
-
-namespace PixiEditor.Helpers.Behaviours
-{
-    internal class TextBoxFocusBehavior : Behavior<TextBox>
-    {
-        // Using a DependencyProperty as the backing store for FillSize.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty FillSizeProperty =
-            DependencyProperty.Register("FillSize", typeof(bool), typeof(TextBoxFocusBehavior),
-                new PropertyMetadata(false));
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Interactivity;
 
 
-        private string oldText; // Value of textbox before editing
-        private bool valueConverted; // This bool is used to avoid double convertion if enter is hitted
-
-        public bool FillSize
-        {
-            get => (bool)GetValue(FillSizeProperty);
-            set => SetValue(FillSizeProperty, value);
-        }
-
-        // Converts number to proper format if enter is clicked and moves focus to next object
-        private void AssociatedObject_KeyUp(object sender, KeyEventArgs e)
-        {
+namespace PixiEditor.Helpers.Behaviours
+{
+    internal class TextBoxFocusBehavior : Behavior<TextBox>
+    {
+        // Using a DependencyProperty as the backing store for FillSize.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty FillSizeProperty =
+            DependencyProperty.Register(
+                "FillSize",
+                typeof(bool),
+                typeof(TextBoxFocusBehavior),
+                new PropertyMetadata(false));
+
+        private string oldText; // Value of textbox before editing
+        private bool valueConverted; // This bool is used to avoid double convertion if enter is hitted
+
+        public bool FillSize
+        {
+            get => (bool)GetValue(FillSizeProperty);
+            set => SetValue(FillSizeProperty, value);
+        }
+
+        protected override void OnAttached()
+        {
+            base.OnAttached();
+            AssociatedObject.GotKeyboardFocus += AssociatedObjectGotKeyboardFocus;
+            AssociatedObject.GotMouseCapture += AssociatedObjectGotMouseCapture;
+            AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObjectPreviewMouseLeftButtonDown;
+            AssociatedObject.LostKeyboardFocus += AssociatedObject_LostKeyboardFocus;
+            AssociatedObject.KeyUp += AssociatedObject_KeyUp;
+        }
+
+        protected override void OnDetaching()
+        {
+            base.OnDetaching();
+            AssociatedObject.GotKeyboardFocus -= AssociatedObjectGotKeyboardFocus;
+            AssociatedObject.GotMouseCapture -= AssociatedObjectGotMouseCapture;
+            AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObjectPreviewMouseLeftButtonDown;
+            AssociatedObject.LostKeyboardFocus -= AssociatedObject_LostKeyboardFocus;
+            AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
+        }
+
+        // Converts number to proper format if enter is clicked and moves focus to next object
+        private void AssociatedObject_KeyUp(object sender, KeyEventArgs e)
+        {
             if (e.Key != Key.Enter)
             if (e.Key != Key.Enter)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            ConvertValue();
-            AssociatedObject.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
-        }
-
-        protected override void OnAttached()
-        {
-            base.OnAttached();
-            AssociatedObject.GotKeyboardFocus += AssociatedObjectGotKeyboardFocus;
-            AssociatedObject.GotMouseCapture += AssociatedObjectGotMouseCapture;
-            AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObjectPreviewMouseLeftButtonDown;
-            AssociatedObject.LostKeyboardFocus += AssociatedObject_LostKeyboardFocus;
-            AssociatedObject.KeyUp += AssociatedObject_KeyUp;
-        }
-
-        protected override void OnDetaching()
-        {
-            base.OnDetaching();
-            AssociatedObject.GotKeyboardFocus -= AssociatedObjectGotKeyboardFocus;
-            AssociatedObject.GotMouseCapture -= AssociatedObjectGotMouseCapture;
-            AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObjectPreviewMouseLeftButtonDown;
-            AssociatedObject.LostKeyboardFocus -= AssociatedObject_LostKeyboardFocus;
-            AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
-        }
-
-        private void AssociatedObjectGotKeyboardFocus(object sender,
-            KeyboardFocusChangedEventArgs e)
-        {
-            AssociatedObject.SelectAll();
-            if (FillSize)
-            {
-                valueConverted = false;
-                oldText = AssociatedObject.Text; // Sets old value when keyboard is focused on object
-            }
-        }
-
-        private void AssociatedObjectGotMouseCapture(object sender,
-            MouseEventArgs e)
-        {
-            AssociatedObject.SelectAll();
-        }
-
-        private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
-        {
-            if (!AssociatedObject.IsKeyboardFocusWithin)
-            {
-                AssociatedObject.Focus();
-                e.Handled = true;
-            }
-        }
-
-        private void AssociatedObject_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
-        {
-            ConvertValue();
-        }
-
-        /// <summary>
-        ///     Converts number from textbox to format "number px" ex. "15 px"
-        /// </summary>
-        private void ConvertValue()
-        {
+            ConvertValue();
+            AssociatedObject.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
+        }
+
+        private void AssociatedObjectGotKeyboardFocus(
+            object sender,
+            KeyboardFocusChangedEventArgs e)
+        {
+            AssociatedObject.SelectAll();
+            if (FillSize)
+            {
+                valueConverted = false;
+                oldText = AssociatedObject.Text; // Sets old value when keyboard is focused on object
+            }
+        }
+
+        private void AssociatedObjectGotMouseCapture(
+            object sender,
+            MouseEventArgs e)
+        {
+            AssociatedObject.SelectAll();
+        }
+
+        private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            if (!AssociatedObject.IsKeyboardFocusWithin)
+            {
+                AssociatedObject.Focus();
+                e.Handled = true;
+            }
+        }
+
+        private void AssociatedObject_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+        {
+            ConvertValue();
+        }
+
+        /// <summary>
+        ///     Converts number from textbox to format "number px" ex. "15 px".
+        /// </summary>
+        private void ConvertValue()
+        {
             if (valueConverted || FillSize == false)
             if (valueConverted || FillSize == false)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            if (int.TryParse(Regex.Replace(AssociatedObject.Text, "\\p{L}", ""), out int result) && result > 0)
+            if (int.TryParse(Regex.Replace(AssociatedObject.Text, "\\p{L}", string.Empty), out int result) && result > 0)
             {
             {
                 AssociatedObject.Text = $"{AssociatedObject.Text} px";
                 AssociatedObject.Text = $"{AssociatedObject.Text} px";
             }
             }
-            else // If text in textbox isn't number, set it to old value
+
+            // If text in textbox isn't number, set it to old value
+            else
             {
             {
                 AssociatedObject.Text = oldText;
                 AssociatedObject.Text = oldText;
             }
             }
 
 
-            valueConverted = true;
-        }
-    }
+            valueConverted = true;
+        }
+    }
 }
 }

+ 24 - 24
PixiEditor/Helpers/Converters/ToolSizeToIntConverter.cs

@@ -1,34 +1,34 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Text.RegularExpressions;
-using System.Windows.Data;
-
-namespace PixiEditor.Helpers
-{
-    [ValueConversion(typeof(string), typeof(int))]
-    internal class ToolSizeToIntConverter : IValueConverter
-    {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return string.Format("{0} {1}", value, "px");
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Windows.Data;
+
+namespace PixiEditor.Helpers
+{
+    [ValueConversion(typeof(string), typeof(int))]
+    internal class ToolSizeToIntConverter : IValueConverter
+    {
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            return string.Format("{0} {1}", value, "px");
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
             if (string.IsNullOrWhiteSpace(value as string))
             if (string.IsNullOrWhiteSpace(value as string))
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            string slicedString = value.ToString().Split(' ').First();
-            slicedString = Regex.Replace(slicedString, "\\p{L}", "");
-            if (slicedString == "")
+            string slicedString = value.ToString().Split(' ').First();
+            slicedString = Regex.Replace(slicedString, "\\p{L}", string.Empty);
+            if (slicedString == string.Empty)
             {
             {
                 return null;
                 return null;
             }
             }
 
 
-            return int.Parse(slicedString);
-        }
-    }
+            return int.Parse(slicedString);
+        }
+    }
 }
 }

+ 18 - 16
PixiEditor/Helpers/Extensions/DictionaryHelper.cs

@@ -1,21 +1,23 @@
-using System.Collections.Generic;
-
-namespace PixiEditor.Helpers.Extensions
-{
-    public static class DictionaryHelper
-    {
-        public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dict,
-            IDictionary<TKey, TValue> dictToAdd)
-        {
+using System.Collections.Generic;
+
+namespace PixiEditor.Helpers.Extensions
+{
+    public static class DictionaryHelper
+    {
+        public static void AddRangeOverride<TKey, TValue>(
+            this IDictionary<TKey, TValue> dict,
+            IDictionary<TKey, TValue> dictToAdd)
+        {
             foreach (KeyValuePair<TKey, TValue> item in dictToAdd)
             foreach (KeyValuePair<TKey, TValue> item in dictToAdd)
             {
             {
                 dict[item.Key] = item.Value;
                 dict[item.Key] = item.Value;
             }
             }
-        }
-
-        public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dict,
-            IDictionary<TKey, TValue> dictToAdd)
-        {
+        }
+
+        public static void AddRangeNewOnly<TKey, TValue>(
+            this IDictionary<TKey, TValue> dict,
+            IDictionary<TKey, TValue> dictToAdd)
+        {
             foreach (KeyValuePair<TKey, TValue> item in dictToAdd)
             foreach (KeyValuePair<TKey, TValue> item in dictToAdd)
             {
             {
                 if (!dict.ContainsKey(item.Key))
                 if (!dict.ContainsKey(item.Key))
@@ -23,6 +25,6 @@ namespace PixiEditor.Helpers.Extensions
                     dict.Add(item.Key, item.Value);
                     dict.Add(item.Key, item.Value);
                 }
                 }
             }
             }
-        }
-    }
+        }
+    }
 }
 }

+ 128 - 127
PixiEditor/Helpers/GlobalMouseHook.cs

@@ -1,134 +1,135 @@
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using System.Windows;
-
-namespace PixiEditor.Helpers
-{
-    // see https://stackoverflow.com/questions/22659925/how-to-capture-mouseup-event-outside-the-wpf-window
-    [ExcludeFromCodeCoverage]
-    public static class GlobalMouseHook
-    {
-        private const int WhMouseLl = 14;
-        private const int WmLbuttonup = 0x0202;
-        private static int mouseHookHandle;
-        private static HookProc mouseDelegate;
-
-        private static event MouseUpEventHandler MouseUp;
-
-        public static event MouseUpEventHandler OnMouseUp
-        {
-            add
-            {
-                Subscribe();
-                MouseUp += value;
-            }
-
-            remove
-            {
-                MouseUp -= value;
-                Unsubscribe();
-            }
-        }
-
-        public static void RaiseMouseUp()
-        {
-            MouseUp?.Invoke(default, default);
-        }
-
-        private static void Unsubscribe()
-        {
-            if (mouseHookHandle != 0)
-            {
-                int result = UnhookWindowsHookEx(mouseHookHandle);
-                mouseHookHandle = 0;
-                mouseDelegate = null;
-                if (result == 0)
-                {
-                    int errorCode = Marshal.GetLastWin32Error();
-                    throw new Win32Exception(errorCode);
-                }
-            }
-        }
-
-        private static void Subscribe()
-        {
-            if (mouseHookHandle == 0)
-            {
-                mouseDelegate = MouseHookProc;
-                mouseHookHandle = SetWindowsHookEx(
-                    WhMouseLl,
-                    mouseDelegate,
-                    GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
-                    0);
-                if (mouseHookHandle == 0)
-                {
-                    int errorCode = Marshal.GetLastWin32Error();
-                    throw new Win32Exception(errorCode);
-                }
-            }
-        }
-
-        private static int MouseHookProc(int nCode, int wParam, IntPtr lParam)
-        {
-            if (nCode >= 0)
-            {
-                Msllhookstruct mouseHookStruct = (Msllhookstruct)Marshal.PtrToStructure(lParam, typeof(Msllhookstruct));
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+namespace PixiEditor.Helpers
+{
+    public delegate void MouseUpEventHandler(object sender, Point p);
+
+    // see https://stackoverflow.com/questions/22659925/how-to-capture-mouseup-event-outside-the-wpf-window
+    [ExcludeFromCodeCoverage]
+    public static class GlobalMouseHook
+    {
+        private const int WhMouseLl = 14;
+        private const int WmLbuttonup = 0x0202;
+        private static int mouseHookHandle;
+        private static HookProc mouseDelegate;
+
+        private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
+
+        public static event MouseUpEventHandler OnMouseUp
+        {
+            add
+            {
+                Subscribe();
+                MouseUp += value;
+            }
+
+            remove
+            {
+                MouseUp -= value;
+                Unsubscribe();
+            }
+        }
+
+        private static event MouseUpEventHandler MouseUp;
+
+        public static void RaiseMouseUp()
+        {
+            MouseUp?.Invoke(default, default);
+        }
+
+        private static void Unsubscribe()
+        {
+            if (mouseHookHandle != 0)
+            {
+                int result = UnhookWindowsHookEx(mouseHookHandle);
+                mouseHookHandle = 0;
+                mouseDelegate = null;
+                if (result == 0)
+                {
+                    int errorCode = Marshal.GetLastWin32Error();
+                    throw new Win32Exception(errorCode);
+                }
+            }
+        }
+
+        private static void Subscribe()
+        {
+            if (mouseHookHandle == 0)
+            {
+                mouseDelegate = MouseHookProc;
+                mouseHookHandle = SetWindowsHookEx(
+                    WhMouseLl,
+                    mouseDelegate,
+                    GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),
+                    0);
+                if (mouseHookHandle == 0)
+                {
+                    int errorCode = Marshal.GetLastWin32Error();
+                    throw new Win32Exception(errorCode);
+                }
+            }
+        }
+
+        private static int MouseHookProc(int nCode, int wParam, IntPtr lParam)
+        {
+            if (nCode >= 0)
+            {
+                Msllhookstruct mouseHookStruct = (Msllhookstruct)Marshal.PtrToStructure(lParam, typeof(Msllhookstruct));
                 if (wParam == WmLbuttonup)
                 if (wParam == WmLbuttonup)
                 {
                 {
                     if (MouseUp != null)
                     if (MouseUp != null)
                     {
                     {
-                        MouseUp.Invoke(null, new System.Windows.Point(mouseHookStruct.pt.x, mouseHookStruct.pt.y));
+                        MouseUp.Invoke(null, new System.Windows.Point(mouseHookStruct.Pt.X, mouseHookStruct.Pt.Y));
                     }
                     }
                 }
                 }
-            }
-
-            return CallNextHookEx(mouseHookHandle, nCode, wParam, lParam);
-        }
-
-        [DllImport("user32.dll",
-            CharSet = CharSet.Auto,
-            CallingConvention = CallingConvention.StdCall,
-            SetLastError = true)]
-        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
-
-        [DllImport(
-            "user32.dll",
-            CharSet = CharSet.Auto,
-            CallingConvention = CallingConvention.StdCall,
-            SetLastError = true)]
-        private static extern int UnhookWindowsHookEx(int idHook);
-
-        [DllImport(
-            "user32.dll",
-            CharSet = CharSet.Auto,
-            CallingConvention = CallingConvention.StdCall)]
-        private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
-
-        [DllImport("kernel32.dll")]
-        private static extern IntPtr GetModuleHandle(string name);
-
-        private delegate int HookProc(int nCode, int wParam, IntPtr lParam);
-
-        [StructLayout(LayoutKind.Sequential)]
-        private struct Point
-        {
-            public readonly int x;
-            public readonly int y;
-        }
-
-        [StructLayout(LayoutKind.Sequential)]
-        private struct Msllhookstruct
-        {
-            public readonly Point pt;
-            public readonly uint mouseData;
-            public readonly uint flags;
-            public readonly uint time;
-            public readonly IntPtr dwExtraInfo;
-        }
-    }
-
-    public delegate void MouseUpEventHandler(object sender, Point p);
+            }
+
+            return CallNextHookEx(mouseHookHandle, nCode, wParam, lParam);
+        }
+
+        [DllImport(
+            "user32.dll",
+            CharSet = CharSet.Auto,
+            CallingConvention = CallingConvention.StdCall,
+            SetLastError = true)]
+        private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
+
+        [DllImport(
+            "user32.dll",
+            CharSet = CharSet.Auto,
+            CallingConvention = CallingConvention.StdCall,
+            SetLastError = true)]
+        private static extern int UnhookWindowsHookEx(int idHook);
+
+        [DllImport(
+            "user32.dll",
+            CharSet = CharSet.Auto,
+            CallingConvention = CallingConvention.StdCall)]
+        private static extern int CallNextHookEx(int idHook, int nCode, int wParam, IntPtr lParam);
+
+        [DllImport("kernel32.dll")]
+        private static extern IntPtr GetModuleHandle(string name);
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct Point
+        {
+            public readonly int X;
+            public readonly int Y;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct Msllhookstruct
+        {
+            public readonly Point Pt;
+            public readonly uint MouseData;
+            public readonly uint Flags;
+            public readonly uint Time;
+            public readonly IntPtr DwExtraInfo;
+        }
+    }
 }
 }

+ 37 - 49
PixiEditor/Helpers/RelayCommand.cs

@@ -1,55 +1,43 @@
-using System;
-using System.Windows.Input;
-
-namespace PixiEditor.Helpers
-{
-    public class RelayCommand : ICommand
-    {
-        #region Fields
-
-        private readonly Action<object> execute;
-        private readonly Predicate<object> canExecute;
-
-        #endregion // Fields
-
-        #region Constructors
-
-        public RelayCommand(Action<object> execute)
-            : this(execute, null)
-        {
-        }
-
-        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
-        {
+using System;
+using System.Windows.Input;
+
+namespace PixiEditor.Helpers
+{
+    public class RelayCommand : ICommand
+    {
+        private readonly Action<object> execute;
+        private readonly Predicate<object> canExecute;
+
+        public RelayCommand(Action<object> execute)
+            : this(execute, null)
+        {
+        }
+
+        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
+        {
             if (execute == null)
             if (execute == null)
             {
             {
                 throw new ArgumentNullException("execute");
                 throw new ArgumentNullException("execute");
             }
             }
 
 
-            this.execute = execute;
-            this.canExecute = canExecute;
-        }
-
-        #endregion // Constructors
-
-        #region ICommand Members
-
-        public bool CanExecute(object parameter)
-        {
-            return canExecute == null ? true : canExecute(parameter);
-        }
-
-        public event EventHandler CanExecuteChanged
-        {
-            add => CommandManager.RequerySuggested += value;
-            remove => CommandManager.RequerySuggested -= value;
-        }
-
-        public void Execute(object parameter)
-        {
-            execute(parameter);
-        }
-
-        #endregion // ICommand Members
-    }
+            this.execute = execute;
+            this.canExecute = canExecute;
+        }
+
+        public event EventHandler CanExecuteChanged
+        {
+            add => CommandManager.RequerySuggested += value;
+            remove => CommandManager.RequerySuggested -= value;
+        }
+
+        public bool CanExecute(object parameter)
+        {
+            return canExecute == null ? true : canExecute(parameter);
+        }
+
+        public void Execute(object parameter)
+        {
+            execute(parameter);
+        }
+    }
 }
 }

+ 110 - 113
PixiEditor/Models/Colors/ExColor.cs

@@ -1,77 +1,77 @@
-using System;
-using System.Windows.Media;
-
-namespace PixiEditor.Models.Colors
-{
-    public static class ExColor
-    {
-        /// <summary>
-        ///     Creates color with corrected brightness.
-        /// </summary>
-        /// <param name="color">Color to correct.</param>
-        /// <param name="correctionFactor">
-        ///     The brightness correction factor. Must be between -1 and 1.
-        ///     Negative values produce darker colors.
-        /// </param>
-        /// <returns>
-        ///     Corrected <see cref="Color" /> structure.
-        /// </returns>
-        public static Color ChangeColorBrightness(Color color, float correctionFactor)
-        {
-            Tuple<int, float, float> hsl = RgbToHsl(color.R, color.G, color.B);
-            int h = hsl.Item1;
-            float s = hsl.Item2;
-            float l = hsl.Item3;
-
-            l = Math.Clamp(l + correctionFactor, 0, 100);
-            Color rgb = HslToRgb(h, s, l);
-
-            return Color.FromArgb(color.A, rgb.R, rgb.G, rgb.B);
-        }
-
-        /// <summary>
-        ///     Converts RGB to HSL
-        /// </summary>
-        /// <param name="r">Red value</param>
-        /// <param name="b">Blue value</param>
-        /// <param name="g">Green value</param>
-        /// <returns>Tuple with 3 values in order: h, s, l0</returns>
-        public static Tuple<int, float, float> RgbToHsl(int r, int g, int b)
-        {
-            int h;
-            float s, l;
-            float dR = r / 255.0f;
-            float dG = g / 255.0f;
-            float dB = b / 255.0f;
-
-            float min = Math.Min(Math.Min(dR, dG), dB);
-            float max = Math.Max(Math.Max(dR, dG), dB);
-            float delta = max - min;
-
-            l = (max + min) / 2;
-
-            if (delta == 0)
-            {
-                h = 0;
-                s = 0.0f;
-            }
-            else
-            {
-                s = l <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
-
-                float hue;
-
+using System;
+using System.Windows.Media;
+
+namespace PixiEditor.Models.Colors
+{
+    public static class ExColor
+    {
+        /// <summary>
+        ///     Creates color with corrected brightness.
+        /// </summary>
+        /// <param name="color">Color to correct.</param>
+        /// <param name="correctionFactor">
+        ///     The brightness correction factor. Must be between -1 and 1.
+        ///     Negative values produce darker colors.
+        /// </param>
+        /// <returns>
+        ///     Corrected <see cref="Color" /> structure.
+        /// </returns>
+        public static Color ChangeColorBrightness(Color color, float correctionFactor)
+        {
+            Tuple<int, float, float> hsl = RgbToHsl(color.R, color.G, color.B);
+            int h = hsl.Item1;
+            float s = hsl.Item2;
+            float l = hsl.Item3;
+
+            l = Math.Clamp(l + correctionFactor, 0, 100);
+            Color rgb = HslToRgb(h, s, l);
+
+            return Color.FromArgb(color.A, rgb.R, rgb.G, rgb.B);
+        }
+
+        /// <summary>
+        ///     Converts RGB to HSL.
+        /// </summary>
+        /// <param name="r">Red value.</param>
+        /// <param name="g">Green value.</param>
+        /// <param name="b">Blue value.</param>
+        /// <returns>Tuple with 3 values in order: h, s, l0.</returns>
+        public static Tuple<int, float, float> RgbToHsl(int r, int g, int b)
+        {
+            int h;
+            float s, l;
+            float dR = r / 255.0f;
+            float dG = g / 255.0f;
+            float dB = b / 255.0f;
+
+            float min = Math.Min(Math.Min(dR, dG), dB);
+            float max = Math.Max(Math.Max(dR, dG), dB);
+            float delta = max - min;
+
+            l = (max + min) / 2;
+
+            if (delta == 0)
+            {
+                h = 0;
+                s = 0.0f;
+            }
+            else
+            {
+                s = l <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
+
+                float hue;
+
                 if (dR == max)
                 if (dR == max)
                 {
                 {
                     hue = (dG - dB) / 6 / delta;
                     hue = (dG - dB) / 6 / delta;
                 }
                 }
                 else if (dG == max)
                 else if (dG == max)
                 {
                 {
-                    hue = 1.0f / 3 + (dB - dR) / 6 / delta;
+                    hue = (1.0f / 3) + ((dB - dR) / 6 / delta);
                 }
                 }
                 else
                 else
                 {
                 {
-                    hue = 2.0f / 3 + (dR - dG) / 6 / delta;
+                    hue = (2.0f / 3) + ((dR - dG) / 6 / delta);
                 }
                 }
 
 
                 if (hue < 0)
                 if (hue < 0)
@@ -84,49 +84,46 @@ namespace PixiEditor.Models.Colors
                     hue -= 1;
                     hue -= 1;
                 }
                 }
 
 
-                h = (int)(hue * 360);
-            }
-
-            return new Tuple<int, float, float>(h, s * 100, l * 100);
-        }
-
-        /// <summary>
-        ///     Converts HSL color format to RGB
-        /// </summary>
-        /// <param name="h"></param>
-        /// <param name="s"></param>
-        /// <param name="l"></param>
-        /// <returns>RGB Color</returns>
-        public static Color HslToRgb(int h, float s, float l)
-        {
-            s /= 100;
-            l /= 100;
-            byte r = 0;
-            byte g = 0;
-            byte b = 0;
-
-            if (s == 0)
-            {
-                r = g = b = (byte)(l * 255);
-            }
-            else
-            {
-                float v1, v2;
-                float hue = (float)h / 360;
-
-                v2 = l < 0.5 ? l * (1 + s) : l + s - l * s;
-                v1 = 2 * l - v2;
-
-                r = (byte)(255 * HueToRgb(v1, v2, hue + 1.0f / 3));
-                g = (byte)(255 * HueToRgb(v1, v2, hue));
-                b = (byte)(255 * HueToRgb(v1, v2, hue - 1.0f / 3));
-            }
-
-            return Color.FromRgb(r, g, b);
-        }
-
-        private static float HueToRgb(float v1, float v2, float hue)
-        {
+                h = (int)(hue * 360);
+            }
+
+            return new Tuple<int, float, float>(h, s * 100, l * 100);
+        }
+
+        /// <summary>
+        ///     Converts HSL color format to RGB.
+        /// </summary>
+        /// <returns>RGB Color.</returns>
+        public static Color HslToRgb(int h, float s, float l)
+        {
+            s /= 100;
+            l /= 100;
+            byte r = 0;
+            byte g = 0;
+            byte b = 0;
+
+            if (s == 0)
+            {
+                r = g = b = (byte)(l * 255);
+            }
+            else
+            {
+                float v1, v2;
+                float hue = (float)h / 360;
+
+                v2 = l < 0.5 ? l * (1 + s) : l + s - (l * s);
+                v1 = (2 * l) - v2;
+
+                r = (byte)(255 * HueToRgb(v1, v2, hue + (1.0f / 3)));
+                g = (byte)(255 * HueToRgb(v1, v2, hue));
+                b = (byte)(255 * HueToRgb(v1, v2, hue - (1.0f / 3)));
+            }
+
+            return Color.FromRgb(r, g, b);
+        }
+
+        private static float HueToRgb(float v1, float v2, float hue)
+        {
             if (hue < 0)
             if (hue < 0)
             {
             {
                 hue += 1;
                 hue += 1;
@@ -139,7 +136,7 @@ namespace PixiEditor.Models.Colors
 
 
             if (6 * hue < 1)
             if (6 * hue < 1)
             {
             {
-                return v1 + (v2 - v1) * 6 * hue;
+                return v1 + ((v2 - v1) * 6 * hue);
             }
             }
 
 
             if (2 * hue < 1)
             if (2 * hue < 1)
@@ -149,10 +146,10 @@ namespace PixiEditor.Models.Colors
 
 
             if (3 * hue < 2)
             if (3 * hue < 2)
             {
             {
-                return v1 + (v2 - v1) * (2.0f / 3 - hue) * 6;
+                return v1 + ((v2 - v1) * ((2.0f / 3) - hue) * 6);
             }
             }
 
 
-            return v1;
-        }
-    }
+            return v1;
+        }
+    }
 }
 }

+ 18 - 0
PixiEditor/Models/Controllers/BitmapChangedEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+using PixiEditor.Models.DataHolders;
+
+public class BitmapChangedEventArgs : EventArgs
+{
+    public BitmapChangedEventArgs(BitmapPixelChanges pixelsChanged, BitmapPixelChanges oldPixelsValues, int changedLayerIndex)
+    {
+        PixelsChanged = pixelsChanged;
+        OldPixelsValues = oldPixelsValues;
+        ChangedLayerIndex = changedLayerIndex;
+    }
+
+    public BitmapPixelChanges PixelsChanged { get; set; }
+
+    public BitmapPixelChanges OldPixelsValues { get; set; }
+
+    public int ChangedLayerIndex { get; set; }
+}

+ 243 - 256
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -1,153 +1,161 @@
-using System;
-using System.Linq;
-using System.Windows;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using PixiEditor.Helpers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Enums;
-using PixiEditor.Models.Events;
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
-
-namespace PixiEditor.Models.Controllers
-{
-    public class BitmapManager : NotifyableObject
-    {
-        private Document activeDocument;
-
-        private Layer previewLayer;
-        private Tool selectedTool;
-
-        public BitmapManager()
-        {
-            MouseController = new MouseMovementController();
-            MouseController.StartedRecordingChanges += MouseController_StartedRecordingChanges;
-            MouseController.MousePositionChanged += Controller_MousePositionChanged;
-            MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
-            BitmapOperations = new BitmapOperationsUtility(this);
-            ReadonlyToolUtility = new ReadonlyToolUtility();
-        }
-
-        public MouseMovementController MouseController { get; set; }
-
-        public Tool SelectedTool
-        {
-            get => selectedTool;
-            private set
-            {
-                selectedTool = value;
-                RaisePropertyChanged("SelectedTool");
-            }
-        }
-
-        public Layer PreviewLayer
-        {
-            get => previewLayer;
-            set
-            {
-                previewLayer = value;
-                RaisePropertyChanged("PreviewLayer");
-            }
-        }
-
-        public Layer ActiveLayer => ActiveDocument.ActiveLayer;
-
-        public Color PrimaryColor { get; set; }
-
-        public int ToolSize
-        {
-            get => SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize") != null
-                ? SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize").Value
-                : 1;
-            set
-            {
-                if (SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is var toolSize)
-                {
-                    toolSize.Value = value;
-                    HighlightPixels(MousePositionConverter.CurrentCoordinates);
-                }
-            }
-        }
-
-        public BitmapOperationsUtility BitmapOperations { get; set; }
-
-        public ReadonlyToolUtility ReadonlyToolUtility { get; set; }
-
-        public Document ActiveDocument
-        {
-            get => activeDocument;
-            set
-            {
-                activeDocument = value;
-                RaisePropertyChanged("ActiveDocument");
-                DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value));
-            }
-        }
-
-        public event EventHandler<LayersChangedEventArgs> LayersChanged;
-
-        public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
-
-        public void SetActiveTool(Tool tool)
-        {
-            PreviewLayer = null;
-            SelectedTool?.Toolbar.SaveToolbarSettings();
-            SelectedTool = tool;
-            SelectedTool.Toolbar.LoadSharedSettings();
-        }
-
-        public void SetActiveLayer(int index)
-        {
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Helpers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Events;
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class BitmapManager : NotifyableObject
+    {
+        private Document activeDocument;
+
+        private Layer previewLayer;
+        private Tool selectedTool;
+
+        public BitmapManager()
+        {
+            MouseController = new MouseMovementController();
+            MouseController.StartedRecordingChanges += MouseController_StartedRecordingChanges;
+            MouseController.MousePositionChanged += Controller_MousePositionChanged;
+            MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
+            BitmapOperations = new BitmapOperationsUtility(this);
+            ReadonlyToolUtility = new ReadonlyToolUtility();
+        }
+
+        public event EventHandler<LayersChangedEventArgs> LayersChanged;
+
+        public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
+
+        public MouseMovementController MouseController { get; set; }
+
+        public Tool SelectedTool
+        {
+            get => selectedTool;
+            private set
+            {
+                selectedTool = value;
+                RaisePropertyChanged("SelectedTool");
+            }
+        }
+
+        public Layer PreviewLayer
+        {
+            get => previewLayer;
+            set
+            {
+                previewLayer = value;
+                RaisePropertyChanged("PreviewLayer");
+            }
+        }
+
+        public Layer ActiveLayer => ActiveDocument.ActiveLayer;
+
+        public Color PrimaryColor { get; set; }
+
+        public int ToolSize
+        {
+            get => SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize") != null
+                ? SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize").Value
+                : 1;
+            set
+            {
+                if (SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is var toolSize)
+                {
+                    toolSize.Value = value;
+                    HighlightPixels(MousePositionConverter.CurrentCoordinates);
+                }
+            }
+        }
+
+        public BitmapOperationsUtility BitmapOperations { get; set; }
+
+        public ReadonlyToolUtility ReadonlyToolUtility { get; set; }
+
+        public Document ActiveDocument
+        {
+            get => activeDocument;
+            set
+            {
+                activeDocument = value;
+                RaisePropertyChanged("ActiveDocument");
+                DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value));
+            }
+        }
+
+        /// <summary>
+        ///     Returns if tool is BitmapOperationTool.
+        /// </summary>
+        public static bool IsOperationTool(Tool tool)
+        {
+            return tool is BitmapOperationTool;
+        }
+
+        public void SetActiveTool(Tool tool)
+        {
+            PreviewLayer = null;
+            SelectedTool?.Toolbar.SaveToolbarSettings();
+            SelectedTool = tool;
+            SelectedTool.Toolbar.LoadSharedSettings();
+        }
+
+        public void SetActiveLayer(int index)
+        {
             if (ActiveDocument.ActiveLayerIndex <= ActiveDocument.Layers.Count - 1)
             if (ActiveDocument.ActiveLayerIndex <= ActiveDocument.Layers.Count - 1)
             {
             {
                 ActiveDocument.ActiveLayer.IsActive = false;
                 ActiveDocument.ActiveLayer.IsActive = false;
             }
             }
 
 
-            ActiveDocument.ActiveLayerIndex = index;
-            ActiveDocument.ActiveLayer.IsActive = true;
-            LayersChanged?.Invoke(this, new LayersChangedEventArgs(index, LayerAction.SetActive));
-        }
-
-        public void AddNewLayer(string name, WriteableBitmap bitmap, bool setAsActive = true)
-        {
-            AddNewLayer(name, bitmap.PixelWidth, bitmap.PixelHeight, setAsActive);
-            ActiveDocument.Layers.Last().LayerBitmap = bitmap;
-        }
-
-        public void AddNewLayer(string name, bool setAsActive = true)
-        {
-            AddNewLayer(name, 0, 0, setAsActive);
-        }
-
-        public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
-        {
-            ActiveDocument.Layers.Add(new Layer(name, width, height)
-            {
-                MaxHeight = ActiveDocument.Height,
-                MaxWidth = ActiveDocument.Width
-            });
+            ActiveDocument.ActiveLayerIndex = index;
+            ActiveDocument.ActiveLayer.IsActive = true;
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(index, LayerAction.SetActive));
+        }
+
+        public void AddNewLayer(string name, WriteableBitmap bitmap, bool setAsActive = true)
+        {
+            AddNewLayer(name, bitmap.PixelWidth, bitmap.PixelHeight, setAsActive);
+            ActiveDocument.Layers.Last().LayerBitmap = bitmap;
+        }
+
+        public void AddNewLayer(string name, bool setAsActive = true)
+        {
+            AddNewLayer(name, 0, 0, setAsActive);
+        }
+
+        public void AddNewLayer(string name, int width, int height, bool setAsActive = true)
+        {
+            ActiveDocument.Layers.Add(new Layer(name, width, height)
+            {
+                MaxHeight = ActiveDocument.Height,
+                MaxWidth = ActiveDocument.Width
+            });
             if (setAsActive)
             if (setAsActive)
             {
             {
                 SetActiveLayer(ActiveDocument.Layers.Count - 1);
                 SetActiveLayer(ActiveDocument.Layers.Count - 1);
             }
             }
 
 
-            LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
-        }
-
-        public void RemoveLayer(int layerIndex)
-        {
+            LayersChanged?.Invoke(this, new LayersChangedEventArgs(0, LayerAction.Add));
+        }
+
+        public void RemoveLayer(int layerIndex)
+        {
             if (ActiveDocument.Layers.Count == 0)
             if (ActiveDocument.Layers.Count == 0)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            bool wasActive = ActiveDocument.Layers[layerIndex].IsActive;
-            ActiveDocument.Layers.RemoveAt(layerIndex);
+            bool wasActive = ActiveDocument.Layers[layerIndex].IsActive;
+            ActiveDocument.Layers.RemoveAt(layerIndex);
             if (wasActive)
             if (wasActive)
             {
             {
                 SetActiveLayer(0);
                 SetActiveLayer(0);
@@ -156,143 +164,122 @@ namespace PixiEditor.Models.Controllers
             {
             {
                 SetActiveLayer(ActiveDocument.Layers.Count - 1);
                 SetActiveLayer(ActiveDocument.Layers.Count - 1);
             }
             }
-        }
-
-        private void Controller_MousePositionChanged(object sender, MouseMovementEventArgs e)
-        {
-            SelectedTool.OnMouseMove(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            if (Mouse.LeftButton == MouseButtonState.Pressed && !IsDraggingViewport() && ActiveDocument != null)
-            {
-                ExecuteTool(e.NewPosition, MouseController.ClickedOnCanvas);
-            }
-            else if (Mouse.LeftButton == MouseButtonState.Released)
+        }
+
+        public void ExecuteTool(Coordinates newPosition, bool clickedOnCanvas)
+        {
+            if (SelectedTool.CanStartOutsideCanvas || clickedOnCanvas)
             {
             {
-                HighlightPixels(e.NewPosition);
-            }
-        }
-
-        public void ExecuteTool(Coordinates newPosition, bool clickedOnCanvas)
-        {
-            if (SelectedTool.CanStartOutsideCanvas || clickedOnCanvas)
-            {
                 if (IsOperationTool(SelectedTool))
                 if (IsOperationTool(SelectedTool))
                 {
                 {
-                    BitmapOperations.ExecuteTool(newPosition,
-                        MouseController.LastMouseMoveCoordinates.ToList(), (BitmapOperationTool)SelectedTool);
+                    BitmapOperations.ExecuteTool(
+                        newPosition,
+                        MouseController.LastMouseMoveCoordinates.ToList(),
+                        (BitmapOperationTool)SelectedTool);
                 }
                 }
                 else
                 else
                 {
                 {
-                    ReadonlyToolUtility.ExecuteTool(MouseController.LastMouseMoveCoordinates.ToArray(),
+                    ReadonlyToolUtility.ExecuteTool(
+                        MouseController.LastMouseMoveCoordinates.ToArray(),
                         (ReadonlyTool)SelectedTool);
                         (ReadonlyTool)SelectedTool);
                 }
                 }
-            }
-        }
-
-        private bool IsDraggingViewport()
-        {
-            return Keyboard.IsKeyDown(Key.LeftShift) && !(SelectedTool is ShapeTool);
-        }
-
-        private void MouseController_StartedRecordingChanges(object sender, EventArgs e)
-        {
-            SelectedTool.OnMouseDown(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            PreviewLayer = null;
-        }
-
-        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
-        {
-            SelectedTool.OnMouseUp(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            if (IsOperationTool(SelectedTool) && ((BitmapOperationTool)SelectedTool).RequiresPreviewLayer)
-            {
-                BitmapOperations.StopAction();
             }
             }
-        }
-
-        public void GeneratePreviewLayer()
-        {
+        }
+
+        public void GeneratePreviewLayer()
+        {
             if (ActiveDocument != null)
             if (ActiveDocument != null)
             {
             {
-                PreviewLayer = new Layer("_previewLayer")
-                {
-                    MaxWidth = ActiveDocument.Width,
-                    MaxHeight = ActiveDocument.Height
+                PreviewLayer = new Layer("_previewLayer")
+                {
+                    MaxWidth = ActiveDocument.Width,
+                    MaxHeight = ActiveDocument.Height
                 };
                 };
             }
             }
-        }
-
-        private void HighlightPixels(Coordinates newPosition)
-        {
+        }
+
+        public WriteableBitmap GetCombinedLayersBitmap()
+        {
+            return BitmapUtils.CombineLayers(ActiveDocument.Layers.Where(x => x.IsVisible).ToArray(), ActiveDocument.Width, ActiveDocument.Height);
+        }
+
+        /// <summary>
+        ///     Returns if selected tool is BitmapOperationTool.
+        /// </summary>
+        public bool IsOperationTool()
+        {
+            return IsOperationTool(SelectedTool);
+        }
+
+        private void Controller_MousePositionChanged(object sender, MouseMovementEventArgs e)
+        {
+            SelectedTool.OnMouseMove(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            if (Mouse.LeftButton == MouseButtonState.Pressed && !IsDraggingViewport() && ActiveDocument != null)
+            {
+                ExecuteTool(e.NewPosition, MouseController.ClickedOnCanvas);
+            }
+            else if (Mouse.LeftButton == MouseButtonState.Released)
+            {
+                HighlightPixels(e.NewPosition);
+            }
+        }
+
+        private bool IsDraggingViewport()
+        {
+            return Keyboard.IsKeyDown(Key.LeftShift) && !(SelectedTool is ShapeTool);
+        }
+
+        private void MouseController_StartedRecordingChanges(object sender, EventArgs e)
+        {
+            SelectedTool.OnMouseDown(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            PreviewLayer = null;
+        }
+
+        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
+        {
+            SelectedTool.OnMouseUp(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            if (IsOperationTool(SelectedTool) && ((BitmapOperationTool)SelectedTool).RequiresPreviewLayer)
+            {
+                BitmapOperations.StopAction();
+            }
+        }
+
+        private void HighlightPixels(Coordinates newPosition)
+        {
             if (ActiveDocument == null || ActiveDocument.Layers.Count == 0 || SelectedTool.HideHighlight)
             if (ActiveDocument == null || ActiveDocument.Layers.Count == 0 || SelectedTool.HideHighlight)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            Coordinates[] highlightArea = CoordinatesCalculator.RectangleToCoordinates(
-                CoordinatesCalculator.CalculateThicknessCenter(newPosition, ToolSize));
-            if (CanChangeHighlightOffset(highlightArea))
-            {
-                PreviewLayer.Offset = new Thickness(highlightArea[0].X, highlightArea[0].Y, 0, 0);
-            }
-            else if (!IsInsideBounds(highlightArea))
-            {
-                PreviewLayer = null;
-            }
-            else
-            {
-                GeneratePreviewLayer();
-                PreviewLayer.SetPixels(
-                    BitmapPixelChanges.FromSingleColoredArray(highlightArea, Color.FromArgb(77, 0, 0, 0)));
-            }
-        }
-
-        private bool CanChangeHighlightOffset(Coordinates[] highlightArea)
-        {
-            return highlightArea.Length > 0 && PreviewLayer != null &&
-                   IsInsideBounds(highlightArea) && highlightArea.Length == PreviewLayer.Width * PreviewLayer.Height;
-        }
-
-        private bool IsInsideBounds(Coordinates[] highlightArea)
-        {
-            return highlightArea[0].X <= ActiveDocument.Width - 1 &&
-                   highlightArea[0].Y <= ActiveDocument.Height - 1 &&
-                   highlightArea[^1].X >= 0 && highlightArea[^1].Y >= 0;
-        }
-
-        public WriteableBitmap GetCombinedLayersBitmap()
-        {
-            return BitmapUtils.CombineLayers(ActiveDocument.Layers.Where(x => x.IsVisible).ToArray(), ActiveDocument.Width, ActiveDocument.Height);
-        }
-
-        /// <summary>
-        ///     Returns if selected tool is BitmapOperationTool
-        /// </summary>
-        /// <returns></returns>
-        public bool IsOperationTool()
-        {
-            return IsOperationTool(SelectedTool);
-        }
-
-        /// <summary>
-        ///     Returns if tool is BitmapOperationTool
-        /// </summary>
-        /// <param name="tool"></param>
-        /// <returns></returns>
-        public static bool IsOperationTool(Tool tool)
-        {
-            return tool is BitmapOperationTool;
-        }
-    }
-
-    public class LayersChangedEventArgs : EventArgs
-    {
-        public LayersChangedEventArgs(int layerAffected, LayerAction layerChangeType)
-        {
-            LayerAffected = layerAffected;
-            LayerChangeType = layerChangeType;
-        }
-
-        public int LayerAffected { get; set; }
-
-        public LayerAction LayerChangeType { get; set; }
-    }
+            Coordinates[] highlightArea = CoordinatesCalculator.RectangleToCoordinates(
+                CoordinatesCalculator.CalculateThicknessCenter(newPosition, ToolSize));
+            if (CanChangeHighlightOffset(highlightArea))
+            {
+                PreviewLayer.Offset = new Thickness(highlightArea[0].X, highlightArea[0].Y, 0, 0);
+            }
+            else if (!IsInsideBounds(highlightArea))
+            {
+                PreviewLayer = null;
+            }
+            else
+            {
+                GeneratePreviewLayer();
+                PreviewLayer.SetPixels(
+                    BitmapPixelChanges.FromSingleColoredArray(highlightArea, Color.FromArgb(77, 0, 0, 0)));
+            }
+        }
+
+        private bool CanChangeHighlightOffset(Coordinates[] highlightArea)
+        {
+            return highlightArea.Length > 0 && PreviewLayer != null &&
+                   IsInsideBounds(highlightArea) && highlightArea.Length == PreviewLayer.Width * PreviewLayer.Height;
+        }
+
+        private bool IsInsideBounds(Coordinates[] highlightArea)
+        {
+            return highlightArea[0].X <= ActiveDocument.Width - 1 &&
+                   highlightArea[0].Y <= ActiveDocument.Height - 1 &&
+                   highlightArea[^1].X >= 0 && highlightArea[^1].Y >= 0;
+        }
+    }
 }
 }

+ 21 - 33
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -23,10 +23,10 @@ namespace PixiEditor.Models.Controllers
             Manager = manager;
             Manager = manager;
         }
         }
 
 
-        public BitmapManager Manager { get; set; }
-
         public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
         public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
 
 
+        public BitmapManager Manager { get; set; }
+
         public void DeletePixels(Layer[] layers, Coordinates[] pixels)
         public void DeletePixels(Layer[] layers, Coordinates[] pixels)
         {
         {
             BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, Color.FromArgb(0, 0, 0, 0));
             BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, Color.FromArgb(0, 0, 0, 0));
@@ -45,11 +45,11 @@ namespace PixiEditor.Models.Controllers
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Executes tool Use() method with given parameters. NOTE: mouseMove is reversed inside function!
+        ///     Executes tool Use() method with given parameters. NOTE: mouseMove is reversed inside function!.
         /// </summary>
         /// </summary>
-        /// <param name="newPos">Most recent coordinates</param>
-        /// <param name="mouseMove">Last mouse movement coordinates</param>
-        /// <param name="tool">Tool to execute</param>
+        /// <param name="newPos">Most recent coordinates.</param>
+        /// <param name="mouseMove">Last mouse movement coordinates.</param>
+        /// <param name="tool">Tool to execute.</param>
         public void ExecuteTool(Coordinates newPos, List<Coordinates> mouseMove, BitmapOperationTool tool)
         public void ExecuteTool(Coordinates newPos, List<Coordinates> mouseMove, BitmapOperationTool tool)
         {
         {
             if (Manager.ActiveDocument != null && tool != null && tool.ToolType != ToolType.None)
             if (Manager.ActiveDocument != null && tool != null && tool.ToolType != ToolType.None)
@@ -67,7 +67,7 @@ namespace PixiEditor.Models.Controllers
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Applies pixels from preview layer to selected layer
+        ///     Applies pixels from preview layer to selected layer.
         /// </summary>
         /// </summary>
         public void StopAction()
         public void StopAction()
         {
         {
@@ -82,8 +82,10 @@ namespace PixiEditor.Models.Controllers
 
 
                 BitmapPixelChanges oldValues = ApplyToLayer(layer, lastModifiedLayers[i]).PixelChanges;
                 BitmapPixelChanges oldValues = ApplyToLayer(layer, lastModifiedLayers[i]).PixelChanges;
 
 
-                BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(lastModifiedLayers[i].PixelChanges,
-                    oldValues, lastModifiedLayers[i].LayerIndex));
+                BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
+                    lastModifiedLayers[i].PixelChanges,
+                    oldValues,
+                    lastModifiedLayers[i].LayerIndex));
                 Manager.PreviewLayer = null;
                 Manager.PreviewLayer = null;
             }
             }
         }
         }
@@ -104,8 +106,10 @@ namespace PixiEditor.Models.Controllers
                     Layer layer = Manager.ActiveDocument.Layers[modifiedLayers[i].LayerIndex];
                     Layer layer = Manager.ActiveDocument.Layers[modifiedLayers[i].LayerIndex];
                     oldPixelsValues[i] = ApplyToLayer(layer, modifiedLayers[i]);
                     oldPixelsValues[i] = ApplyToLayer(layer, modifiedLayers[i]);
 
 
-                    BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(modifiedLayers[i].PixelChanges,
-                        oldPixelsValues[i].PixelChanges, modifiedLayers[i].LayerIndex));
+                    BitmapChanged?.Invoke(this, new BitmapChangedEventArgs(
+                        modifiedLayers[i].PixelChanges,
+                        oldPixelsValues[i].PixelChanges,
+                        modifiedLayers[i].LayerIndex));
                 }
                 }
             }
             }
             else
             else
@@ -134,8 +138,6 @@ namespace PixiEditor.Models.Controllers
         /// <summary>
         /// <summary>
         ///     Extracts square from rectangle mouse drag, used to draw symmetric shapes.
         ///     Extracts square from rectangle mouse drag, used to draw symmetric shapes.
         /// </summary>
         /// </summary>
-        /// <param name="mouseMoveCords"></param>
-        /// <returns></returns>
         private List<Coordinates> GetSquareCoordiantes(List<Coordinates> mouseMoveCords)
         private List<Coordinates> GetSquareCoordiantes(List<Coordinates> mouseMoveCords)
         {
         {
             int xLength = mouseMoveCords[0].Y - mouseMoveCords[^1].Y;
             int xLength = mouseMoveCords[0].Y - mouseMoveCords[^1].Y;
@@ -162,7 +164,8 @@ namespace PixiEditor.Models.Controllers
                 Coordinates[] relativeCoords = Manager.ActiveLayer.ConvertToRelativeCoordinates(coordinates);
                 Coordinates[] relativeCoords = Manager.ActiveLayer.ConvertToRelativeCoordinates(coordinates);
                 for (int i = 0; i < coordinates.Length; i++)
                 for (int i = 0; i < coordinates.Length; i++)
                 {
                 {
-                    values.Add(coordinates[i],
+                    values.Add(
+                        coordinates[i],
                         Manager.ActiveLayer.GetPixel(relativeCoords[i].X, relativeCoords[i].Y));
                         Manager.ActiveLayer.GetPixel(relativeCoords[i].X, relativeCoords[i].Y));
                 }
                 }
             }
             }
@@ -176,29 +179,14 @@ namespace PixiEditor.Models.Controllers
             if (mouseMove.Count > 0 && mouseMove[0] != lastMousePos)
             if (mouseMove.Count > 0 && mouseMove[0] != lastMousePos)
             {
             {
                 Manager.GeneratePreviewLayer();
                 Manager.GeneratePreviewLayer();
-                modifiedLayers = ((BitmapOperationTool)Manager.SelectedTool).Use(Manager.ActiveDocument.ActiveLayer,
-                    mouseMove.ToArray(), Manager.PrimaryColor);
+                modifiedLayers = ((BitmapOperationTool)Manager.SelectedTool).Use(
+                    Manager.ActiveDocument.ActiveLayer,
+                    mouseMove.ToArray(),
+                    Manager.PrimaryColor);
                 BitmapPixelChanges[] changes = modifiedLayers.Select(x => x.PixelChanges).ToArray();
                 BitmapPixelChanges[] changes = modifiedLayers.Select(x => x.PixelChanges).ToArray();
                 Manager.PreviewLayer.SetPixels(BitmapPixelChanges.CombineOverride(changes));
                 Manager.PreviewLayer.SetPixels(BitmapPixelChanges.CombineOverride(changes));
                 lastModifiedLayers = modifiedLayers;
                 lastModifiedLayers = modifiedLayers;
             }
             }
         }
         }
     }
     }
-}
-
-public class BitmapChangedEventArgs : EventArgs
-{
-    public BitmapChangedEventArgs(BitmapPixelChanges pixelsChanged, BitmapPixelChanges oldPixelsValues,
-        int changedLayerIndex)
-    {
-        PixelsChanged = pixelsChanged;
-        OldPixelsValues = oldPixelsValues;
-        ChangedLayerIndex = changedLayerIndex;
-    }
-
-    public BitmapPixelChanges PixelsChanged { get; set; }
-
-    public BitmapPixelChanges OldPixelsValues { get; set; }
-
-    public int ChangedLayerIndex { get; set; }
 }
 }

+ 86 - 90
PixiEditor/Models/Controllers/ClipboardController.cs

@@ -1,111 +1,107 @@
-using System.IO;
-using System.Linq;
-using System.Windows;
-using System.Windows.Media.Imaging;
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.ViewModels;
-
-namespace PixiEditor.Models.Controllers
-{
-    public static class ClipboardController
-    {
-        /// <summary>
-        ///     Copies selection to clipboard in PNG, Bitmap and DIB formats.
-        /// </summary>
-        /// <param name="layers">Layers where selection is</param>
-        /// <param name="selection"></param>
-        /// <param name="originalImageWidth">Output </param>
-        /// <param name="originalImageHeight"></param>
-        public static void CopyToClipboard(Layer[] layers, Coordinates[] selection, int originalImageWidth, int originalImageHeight)
-        {
-            Clipboard.Clear();
-            WriteableBitmap combinedBitmaps = BitmapUtils.CombineLayers(layers, originalImageWidth, originalImageHeight);
-            using (MemoryStream pngStream = new MemoryStream())
-            {
-                DataObject data = new DataObject();
-                BitmapSource croppedBmp = BitmapSelectionToBmpSource(combinedBitmaps, selection);
-                data.SetData(DataFormats.Bitmap, croppedBmp, true); // Bitmap, no transparency support
-
-                PngBitmapEncoder encoder = new PngBitmapEncoder();
-                encoder.Frames.Add(BitmapFrame.Create(croppedBmp));
-                encoder.Save(pngStream);
-                data.SetData("PNG", pngStream, false); // PNG, supports transparency
-
-                Clipboard.SetImage(croppedBmp); // DIB format
-                Clipboard.SetDataObject(data, true);
-            }
-        }
-
-        /// <summary>
-        ///     Pastes image from clipboard into new layer.
-        /// </summary>
-        public static void PasteFromClipboard()
-        {
-            WriteableBitmap image = GetImageFromClipboard();
+using System.IO;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media.Imaging;
+using PixiEditor.Models.ImageManipulation;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.ViewModels;
+
+namespace PixiEditor.Models.Controllers
+{
+    public static class ClipboardController
+    {
+        /// <summary>
+        ///     Copies selection to clipboard in PNG, Bitmap and DIB formats.
+        /// </summary>
+        /// <param name="layers">Layers where selection is.</param>
+        public static void CopyToClipboard(Layer[] layers, Coordinates[] selection, int originalImageWidth, int originalImageHeight)
+        {
+            Clipboard.Clear();
+            WriteableBitmap combinedBitmaps = BitmapUtils.CombineLayers(layers, originalImageWidth, originalImageHeight);
+            using (MemoryStream pngStream = new MemoryStream())
+            {
+                DataObject data = new DataObject();
+                BitmapSource croppedBmp = BitmapSelectionToBmpSource(combinedBitmaps, selection);
+                data.SetData(DataFormats.Bitmap, croppedBmp, true); // Bitmap, no transparency support
+
+                PngBitmapEncoder encoder = new PngBitmapEncoder();
+                encoder.Frames.Add(BitmapFrame.Create(croppedBmp));
+                encoder.Save(pngStream);
+                data.SetData("PNG", pngStream, false); // PNG, supports transparency
+
+                Clipboard.SetImage(croppedBmp); // DIB format
+                Clipboard.SetDataObject(data, true);
+            }
+        }
+
+        /// <summary>
+        ///     Pastes image from clipboard into new layer.
+        /// </summary>
+        public static void PasteFromClipboard()
+        {
+            WriteableBitmap image = GetImageFromClipboard();
             if (image != null)
             if (image != null)
             {
             {
                 AddImageToLayers(image);
                 AddImageToLayers(image);
             }
             }
-        }
-
-        /// <summary>
-        ///     Gets image from clipboard, supported PNG, Dib and Bitmap
-        /// </summary>
-        /// <returns>WriteableBitmap</returns>
-        public static WriteableBitmap GetImageFromClipboard()
-        {
-            DataObject dao = (DataObject)Clipboard.GetDataObject();
-            WriteableBitmap finalImage = null;
+        }
+
+        /// <summary>
+        ///     Gets image from clipboard, supported PNG, Dib and Bitmap.
+        /// </summary>
+        /// <returns>WriteableBitmap.</returns>
+        public static WriteableBitmap GetImageFromClipboard()
+        {
+            DataObject dao = (DataObject)Clipboard.GetDataObject();
+            WriteableBitmap finalImage = null;
             if (dao.GetDataPresent("PNG"))
             if (dao.GetDataPresent("PNG"))
             {
             {
-                using (MemoryStream pngStream = dao.GetData("PNG") as MemoryStream)
-                {
-                    if (pngStream != null)
-                    {
-                        PngBitmapDecoder decoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.IgnoreImageCache,
-                            BitmapCacheOption.OnLoad);
-                        finalImage = new WriteableBitmap(decoder.Frames[0].Clone());
-                    }
+                using (MemoryStream pngStream = dao.GetData("PNG") as MemoryStream)
+                {
+                    if (pngStream != null)
+                    {
+                        PngBitmapDecoder decoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
+                        finalImage = new WriteableBitmap(decoder.Frames[0].Clone());
+                    }
                 }
                 }
             }
             }
             else if (dao.GetDataPresent(DataFormats.Dib))
             else if (dao.GetDataPresent(DataFormats.Dib))
             {
             {
-                finalImage = new WriteableBitmap(Clipboard.GetImage()!);
+                finalImage = new WriteableBitmap(Clipboard.GetImage() !);
             }
             }
             else if (dao.GetDataPresent(DataFormats.Bitmap))
             else if (dao.GetDataPresent(DataFormats.Bitmap))
             {
             {
-                finalImage = new WriteableBitmap((dao.GetData(DataFormats.Bitmap) as BitmapSource)!);
+                finalImage = new WriteableBitmap((dao.GetData(DataFormats.Bitmap) as BitmapSource) !);
             }
             }
 
 
-            return finalImage;
-        }
-
-        public static bool IsImageInClipboard()
-        {
-            DataObject dao = (DataObject)Clipboard.GetDataObject();
+            return finalImage;
+        }
+
+        public static bool IsImageInClipboard()
+        {
+            DataObject dao = (DataObject)Clipboard.GetDataObject();
             if (dao == null)
             if (dao == null)
             {
             {
                 return false;
                 return false;
             }
             }
 
 
-            return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
-                   dao.GetDataPresent(DataFormats.Bitmap);
-        }
-
-        private static void AddImageToLayers(WriteableBitmap image)
-        {
-            ViewModelMain.Current.BitmapManager.AddNewLayer("Image", image);
-        }
-
-        public static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection)
-        {
-            int offsetX = selection.Min(x => x.X);
-            int offsetY = selection.Min(x => x.Y);
-            int width = selection.Max(x => x.X) - offsetX + 1;
-            int height = selection.Max(x => x.Y) - offsetY + 1;
-            return bitmap.Crop(offsetX, offsetY, width, height);
-        }
-    }
+            return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
+                   dao.GetDataPresent(DataFormats.Bitmap);
+        }
+
+        public static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection)
+        {
+            int offsetX = selection.Min(x => x.X);
+            int offsetY = selection.Min(x => x.Y);
+            int width = selection.Max(x => x.X) - offsetX + 1;
+            int height = selection.Max(x => x.Y) - offsetY + 1;
+            return bitmap.Crop(offsetX, offsetY, width, height);
+        }
+
+        private static void AddImageToLayers(WriteableBitmap image)
+        {
+            ViewModelMain.Current.BitmapManager.AddNewLayer("Image", image);
+        }
+    }
 }
 }

+ 18 - 0
PixiEditor/Models/Controllers/LayersChangedEventArgs.cs

@@ -0,0 +1,18 @@
+using System;
+using PixiEditor.Models.Enums;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class LayersChangedEventArgs : EventArgs
+    {
+        public LayersChangedEventArgs(int layerAffected, LayerAction layerChangeType)
+        {
+            LayerAffected = layerAffected;
+            LayerChangeType = layerChangeType;
+        }
+
+        public int LayerAffected { get; set; }
+
+        public LayerAction LayerChangeType { get; set; }
+    }
+}

+ 7 - 18
PixiEditor/Models/Controllers/MouseMovementController.cs

@@ -6,18 +6,18 @@ namespace PixiEditor.Models.Controllers
 {
 {
     public class MouseMovementController
     public class MouseMovementController
     {
     {
-        public List<Coordinates> LastMouseMoveCoordinates { get; } = new List<Coordinates>();
-
-        public bool IsRecordingChanges { get; private set; }
-
-        public bool ClickedOnCanvas { get; set; }
-
         public event EventHandler StartedRecordingChanges;
         public event EventHandler StartedRecordingChanges;
 
 
         public event EventHandler<MouseMovementEventArgs> MousePositionChanged;
         public event EventHandler<MouseMovementEventArgs> MousePositionChanged;
 
 
         public event EventHandler StoppedRecordingChanges;
         public event EventHandler StoppedRecordingChanges;
 
 
+        public List<Coordinates> LastMouseMoveCoordinates { get; } = new List<Coordinates>();
+
+        public bool IsRecordingChanges { get; private set; }
+
+        public bool ClickedOnCanvas { get; set; }
+
         public void StartRecordingMouseMovementChanges(bool clickedOnCanvas)
         public void StartRecordingMouseMovementChanges(bool clickedOnCanvas)
         {
         {
             if (IsRecordingChanges == false)
             if (IsRecordingChanges == false)
@@ -42,9 +42,8 @@ namespace PixiEditor.Models.Controllers
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Plain mouse move, does not affect mouse drag recordings
+        ///     Plain mouse move, does not affect mouse drag recordings.
         /// </summary>
         /// </summary>
-        /// <param name="mouseCoordinates"></param>
         public void MouseMoved(Coordinates mouseCoordinates)
         public void MouseMoved(Coordinates mouseCoordinates)
         {
         {
             MousePositionChanged?.Invoke(this, new MouseMovementEventArgs(mouseCoordinates));
             MousePositionChanged?.Invoke(this, new MouseMovementEventArgs(mouseCoordinates));
@@ -60,14 +59,4 @@ namespace PixiEditor.Models.Controllers
             }
             }
         }
         }
     }
     }
-}
-
-public class MouseMovementEventArgs : EventArgs
-{
-    public MouseMovementEventArgs(Coordinates mousePosition)
-    {
-        NewPosition = mousePosition;
-    }
-
-    public Coordinates NewPosition { get; set; }
 }
 }

+ 12 - 0
PixiEditor/Models/Controllers/MouseMovementEventArgs.cs

@@ -0,0 +1,12 @@
+using System;
+using PixiEditor.Models.Position;
+
+public class MouseMovementEventArgs : EventArgs
+{
+    public MouseMovementEventArgs(Coordinates mousePosition)
+    {
+        NewPosition = mousePosition;
+    }
+
+    public Coordinates NewPosition { get; set; }
+}

+ 36 - 36
PixiEditor/Models/Controllers/PixelChangesController.cs

@@ -12,10 +12,10 @@ namespace PixiEditor.Models.Controllers
         private Dictionary<int, LayerChange> LastOldValues { get; set; }
         private Dictionary<int, LayerChange> LastOldValues { get; set; }
 
 
         /// <summary>
         /// <summary>
-        ///     Adds layer changes to controller
+        ///     Adds layer changes to controller.
         /// </summary>
         /// </summary>
-        /// <param name="changes">New changes</param>
-        /// <param name="oldValues">Old values of changes</param>
+        /// <param name="changes">New changes.</param>
+        /// <param name="oldValues">Old values of changes.</param>
         public void AddChanges(LayerChange changes, LayerChange oldValues)
         public void AddChanges(LayerChange changes, LayerChange oldValues)
         {
         {
             if (changes.PixelChanges.ChangedPixels.Count > 0)
             if (changes.PixelChanges.ChangedPixels.Count > 0)
@@ -36,6 +36,39 @@ namespace PixiEditor.Models.Controllers
             }
             }
         }
         }
 
 
+        /// <summary>
+        ///     Returns all changes and deletes them from controller.
+        /// </summary>
+        /// <returns>Tuple array with new changes and old values.</returns>
+        public Tuple<LayerChange, LayerChange>[] PopChanges()
+        {
+            // Maybe replace Tuple with custom data type
+            if (LastChanges == null)
+            {
+                return null;
+            }
+
+            Tuple<LayerChange, LayerChange>[] result = new Tuple<LayerChange, LayerChange>[LastChanges.Count];
+            int i = 0;
+            foreach (KeyValuePair<int, LayerChange> change in LastChanges)
+            {
+                Dictionary<Position.Coordinates, System.Windows.Media.Color> pixelChanges =
+                    change.Value.PixelChanges.ChangedPixels.ToDictionary(entry => entry.Key, entry => entry.Value);
+                Dictionary<Position.Coordinates, System.Windows.Media.Color> oldValues = LastOldValues[change.Key].PixelChanges.ChangedPixels
+                    .ToDictionary(entry => entry.Key, entry => entry.Value);
+
+                LayerChange tmp = new LayerChange(new BitmapPixelChanges(pixelChanges), change.Key);
+                LayerChange oldValuesTmp = new LayerChange(new BitmapPixelChanges(oldValues), change.Key);
+
+                result[i] = new Tuple<LayerChange, LayerChange>(tmp, oldValuesTmp);
+                i++;
+            }
+
+            LastChanges = null;
+            LastOldValues = null;
+            return result;
+        }
+
         private void AddNewLayerChange(LayerChange changes, LayerChange oldValues)
         private void AddNewLayerChange(LayerChange changes, LayerChange oldValues)
         {
         {
             LastChanges[changes.LayerIndex] = changes;
             LastChanges[changes.LayerIndex] = changes;
@@ -68,38 +101,5 @@ namespace PixiEditor.Models.Controllers
                 }
                 }
             }
             }
         }
         }
-
-        /// <summary>
-        ///     Returns all changes and deletes them from controller.
-        /// </summary>
-        /// <returns>Tuple array with new changes and old values</returns>
-        public Tuple<LayerChange, LayerChange>[] PopChanges()
-        {
-            // Maybe replace Tuple with custom data type
-            if (LastChanges == null)
-            {
-                return null;
-            }
-
-            Tuple<LayerChange, LayerChange>[] result = new Tuple<LayerChange, LayerChange>[LastChanges.Count];
-            int i = 0;
-            foreach (KeyValuePair<int, LayerChange> change in LastChanges)
-            {
-                Dictionary<Position.Coordinates, System.Windows.Media.Color> pixelChanges =
-                    change.Value.PixelChanges.ChangedPixels.ToDictionary(entry => entry.Key, entry => entry.Value);
-                Dictionary<Position.Coordinates, System.Windows.Media.Color> oldValues = LastOldValues[change.Key].PixelChanges.ChangedPixels
-                    .ToDictionary(entry => entry.Key, entry => entry.Value);
-
-                LayerChange tmp = new LayerChange(new BitmapPixelChanges(pixelChanges), change.Key);
-                LayerChange oldValuesTmp = new LayerChange(new BitmapPixelChanges(oldValues), change.Key);
-
-                result[i] = new Tuple<LayerChange, LayerChange>(tmp, oldValuesTmp);
-                i++;
-            }
-
-            LastChanges = null;
-            LastOldValues = null;
-            return result;
-        }
     }
     }
 }
 }

+ 1 - 2
PixiEditor/Models/Controllers/Shortcuts/Shortcut.cs

@@ -4,8 +4,7 @@ namespace PixiEditor.Models.Controllers.Shortcuts
 {
 {
     public class Shortcut
     public class Shortcut
     {
     {
-        public Shortcut(Key shortcutKey, ICommand command, object commandParameter = null,
-            ModifierKeys modifier = ModifierKeys.None)
+        public Shortcut(Key shortcutKey, ICommand command, object commandParameter = null, ModifierKeys modifier = ModifierKeys.None)
         {
         {
             ShortcutKey = shortcutKey;
             ShortcutKey = shortcutKey;
             Modifier = modifier;
             Modifier = modifier;

+ 71 - 71
PixiEditor/Models/Controllers/UndoManager.cs

@@ -1,55 +1,55 @@
-using System.Collections.Generic;
-using System.Linq;
-using PixiEditor.Models.DataHolders;
-
-namespace PixiEditor.Models.Controllers
-{
-    public static class UndoManager
-    {
-        private static bool lastChangeWasUndo;
+using System.Collections.Generic;
+using System.Linq;
+using PixiEditor.Models.DataHolders;
 
 
-        public static Stack<Change> UndoStack { get; set; } = new Stack<Change>();
+namespace PixiEditor.Models.Controllers
+{
+    public static class UndoManager
+    {
+        private static bool lastChangeWasUndo;
 
 
-        public static Stack<Change> RedoStack { get; set; } = new Stack<Change>();
-
-        public static bool CanUndo => UndoStack.Count > 0;
-
-        public static bool CanRedo => RedoStack.Count > 0;
-
-        public static object MainRoot { get; set; }
-
-        /// <summary>
-        ///     Sets object(root) in which undo properties are stored.
-        /// </summary>
-        /// <param name="root">Parent object.</param>
-        public static void SetMainRoot(object root)
-        {
-            MainRoot = root;
-        }
-
-        /// <summary>
-        ///     Adds property change to UndoStack
-        /// </summary>
-        /// <param name="change"></param>
-        public static void AddUndoChange(Change change)
-        {
-            if (lastChangeWasUndo == false && RedoStack.Count > 0) // Clears RedoStack if las move wasn't redo or undo and if redo stack is greater than 0
+        public static Stack<Change> UndoStack { get; set; } = new Stack<Change>();
+
+        public static Stack<Change> RedoStack { get; set; } = new Stack<Change>();
+
+        public static bool CanUndo => UndoStack.Count > 0;
+
+        public static bool CanRedo => RedoStack.Count > 0;
+
+        public static object MainRoot { get; set; }
+
+        /// <summary>
+        ///     Sets object(root) in which undo properties are stored.
+        /// </summary>
+        /// <param name="root">Parent object.</param>
+        public static void SetMainRoot(object root)
+        {
+            MainRoot = root;
+        }
+
+        /// <summary>
+        ///     Adds property change to UndoStack.
+        /// </summary>
+        public static void AddUndoChange(Change change)
+        {
+            // Clears RedoStack if las move wasn't redo or undo and if redo stack is greater than 0
+            if (lastChangeWasUndo == false && RedoStack.Count > 0)
             {
             {
                 RedoStack.Clear();
                 RedoStack.Clear();
             }
             }
 
 
-            lastChangeWasUndo = false;
-            change.Root ??= MainRoot;
-            UndoStack.Push(change);
-        }
-
-        /// <summary>
-        ///     Sets top property in UndoStack to Old Value
-        /// </summary>
-        public static void Undo()
-        {
-            lastChangeWasUndo = true;
-            Change change = UndoStack.Pop();
+            lastChangeWasUndo = false;
+            change.Root ??= MainRoot;
+            UndoStack.Push(change);
+        }
+
+        /// <summary>
+        ///     Sets top property in UndoStack to Old Value.
+        /// </summary>
+        public static void Undo()
+        {
+            lastChangeWasUndo = true;
+            Change change = UndoStack.Pop();
             if (change.ReverseProcess == null)
             if (change.ReverseProcess == null)
             {
             {
                 SetPropertyValue(change.Root, change.Property, change.OldValue);
                 SetPropertyValue(change.Root, change.Property, change.OldValue);
@@ -59,16 +59,16 @@ namespace PixiEditor.Models.Controllers
                 change.ReverseProcess(change.ReverseProcessArguments);
                 change.ReverseProcess(change.ReverseProcessArguments);
             }
             }
 
 
-            RedoStack.Push(change);
-        }
-
-        /// <summary>
-        ///     Sets top property from RedoStack to old value
-        /// </summary>
-        public static void Redo()
-        {
-            lastChangeWasUndo = true;
-            Change change = RedoStack.Pop();
+            RedoStack.Push(change);
+        }
+
+        /// <summary>
+        ///     Sets top property from RedoStack to old value.
+        /// </summary>
+        public static void Redo()
+        {
+            lastChangeWasUndo = true;
+            Change change = RedoStack.Pop();
             if (change.Process == null)
             if (change.Process == null)
             {
             {
                 SetPropertyValue(change.Root, change.Property, change.NewValue);
                 SetPropertyValue(change.Root, change.Property, change.NewValue);
@@ -78,20 +78,20 @@ namespace PixiEditor.Models.Controllers
                 change.Process(change.ProcessArguments);
                 change.Process(change.ProcessArguments);
             }
             }
 
 
-            UndoStack.Push(change);
-        }
+            UndoStack.Push(change);
+        }
+
+        private static void SetPropertyValue(object target, string propName, object value)
+        {
+            string[] bits = propName.Split('.');
+            for (int i = 0; i < bits.Length - 1; i++)
+            {
+                System.Reflection.PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
+                target = propertyToGet.GetValue(target, null);
+            }
 
 
-        private static void SetPropertyValue(object target, string propName, object value)
-        {
-            string[] bits = propName.Split('.');
-            for (int i = 0; i < bits.Length - 1; i++)
-            {
-                System.Reflection.PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
-                target = propertyToGet.GetValue(target, null);
-            }
-
-            System.Reflection.PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last());
-            propertyToSet.SetValue(target, value, null);
-        }
-    }
+            System.Reflection.PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last());
+            propertyToSet.SetValue(target, value, null);
+        }
+    }
 }
 }

+ 56 - 61
PixiEditor/Models/DataHolders/BitmapPixelChanges.cs

@@ -1,88 +1,83 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows.Media;
-using PixiEditor.Exceptions;
-using PixiEditor.Helpers.Extensions;
-using PixiEditor.Models.Position;
-
-namespace PixiEditor.Models.DataHolders
-{
-    public struct BitmapPixelChanges
-    {
-        public bool WasBuiltAsSingleColored { get; private set; }
-
-        public static BitmapPixelChanges Empty => new BitmapPixelChanges(new Dictionary<Coordinates, Color>());
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Media;
+using PixiEditor.Exceptions;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Position;
 
 
-        public Dictionary<Coordinates, Color> ChangedPixels { get; set; }
-
-        public BitmapPixelChanges(Dictionary<Coordinates, Color> changedPixels)
-        {
-            ChangedPixels = changedPixels;
-            WasBuiltAsSingleColored = false;
-        }
-
-        /// <summary>
-        ///     Builds BitmapPixelChanges with only one color for specified coordinates
-        /// </summary>
-        /// <param name="coordinates"></param>
-        /// <param name="color"></param>
-        /// <returns>Single-colored BitmapPixelChanges</returns>
-        public static BitmapPixelChanges FromSingleColoredArray(IEnumerable<Coordinates> coordinates, Color color)
-        {
-            Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
+namespace PixiEditor.Models.DataHolders
+{
+    public struct BitmapPixelChanges
+    {
+        public BitmapPixelChanges(Dictionary<Coordinates, Color> changedPixels)
+        {
+            ChangedPixels = changedPixels;
+            WasBuiltAsSingleColored = false;
+        }
+
+        public static BitmapPixelChanges Empty => new BitmapPixelChanges(new Dictionary<Coordinates, Color>());
+
+        public bool WasBuiltAsSingleColored { get; private set; }
+
+        public Dictionary<Coordinates, Color> ChangedPixels { get; set; }
+
+        /// <summary>
+        ///     Builds BitmapPixelChanges with only one color for specified coordinates.
+        /// </summary>
+        /// <returns>Single-colored BitmapPixelChanges.</returns>
+        public static BitmapPixelChanges FromSingleColoredArray(IEnumerable<Coordinates> coordinates, Color color)
+        {
+            Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
             foreach (Coordinates coordinate in coordinates)
             foreach (Coordinates coordinate in coordinates)
             {
             {
                 dict.Add(coordinate, color);
                 dict.Add(coordinate, color);
             }
             }
 
 
-            return new BitmapPixelChanges(dict) { WasBuiltAsSingleColored = true };
-        }
-
-        /// <summary>
-        ///     Combines pixel changes array with overriding values.
-        /// </summary>
-        /// <param name="changes">BitmapPixelChanges to combine</param>
-        /// <returns>Combined BitmapPixelChanges</returns>
-        public static BitmapPixelChanges CombineOverride(BitmapPixelChanges[] changes)
-        {
+            return new BitmapPixelChanges(dict) { WasBuiltAsSingleColored = true };
+        }
+
+        /// <summary>
+        ///     Combines pixel changes array with overriding values.
+        /// </summary>
+        /// <param name="changes">BitmapPixelChanges to combine.</param>
+        /// <returns>Combined BitmapPixelChanges.</returns>
+        public static BitmapPixelChanges CombineOverride(BitmapPixelChanges[] changes)
+        {
             if (changes == null || changes.Length == 0)
             if (changes == null || changes.Length == 0)
             {
             {
                 throw new ArgumentException();
                 throw new ArgumentException();
             }
             }
 
 
-            BitmapPixelChanges output = Empty;
-
+            BitmapPixelChanges output = Empty;
+
             for (int i = 0; i < changes.Length; i++)
             for (int i = 0; i < changes.Length; i++)
             {
             {
                 output.ChangedPixels.AddRangeOverride(changes[i].ChangedPixels);
                 output.ChangedPixels.AddRangeOverride(changes[i].ChangedPixels);
             }
             }
 
 
-            return output;
-        }
-
-        /// <summary>
-        ///     Builds BitmapPixelChanges using 2 same-length enumerables of coordinates and colors
-        /// </summary>
-        /// <param name="coordinates"></param>
-        /// <param name="color"></param>
-        /// <returns></returns>
-        public static BitmapPixelChanges FromArrays(IEnumerable<Coordinates> coordinates, IEnumerable<Color> color)
-        {
-            Coordinates[] coordinateArray = coordinates.ToArray();
-            Color[] colorArray = color.ToArray();
+            return output;
+        }
+
+        /// <summary>
+        ///     Builds BitmapPixelChanges using 2 same-length enumerables of coordinates and colors.
+        /// </summary>
+        public static BitmapPixelChanges FromArrays(IEnumerable<Coordinates> coordinates, IEnumerable<Color> color)
+        {
+            Coordinates[] coordinateArray = coordinates.ToArray();
+            Color[] colorArray = color.ToArray();
             if (coordinateArray.Length != colorArray.Length)
             if (coordinateArray.Length != colorArray.Length)
             {
             {
                 throw new ArrayLengthMismatchException();
                 throw new ArrayLengthMismatchException();
             }
             }
 
 
-            Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
+            Dictionary<Coordinates, Color> dict = new Dictionary<Coordinates, Color>();
             for (int i = 0; i < coordinateArray.Length; i++)
             for (int i = 0; i < coordinateArray.Length; i++)
             {
             {
                 dict.Add(coordinateArray[i], colorArray[i]);
                 dict.Add(coordinateArray[i], colorArray[i]);
             }
             }
 
 
-            return new BitmapPixelChanges(dict);
-        }
-    }
+            return new BitmapPixelChanges(dict);
+        }
+    }
 }
 }

+ 95 - 78
PixiEditor/Models/DataHolders/Change.cs

@@ -1,81 +1,98 @@
-using System;
-
-namespace PixiEditor.Models.DataHolders
-{
-    [Serializable]
-    public class Change
-    {
-        public object[] ProcessArguments;
-        public object[] ReverseProcessArguments;
-
-        /// <summary>
-        ///     Creates new change for property based undo system
-        /// </summary>
-        /// <param name="property">Name of property</param>
-        /// <param name="oldValue">Old value of property</param>
-        /// <param name="newValue">New value of property</param>
-        /// <param name="description">Description of change</param>
-        /// <param name="root">Custom root for finding property</param>
-        public Change(string property, object oldValue, object newValue,
-            string description = "", object root = null)
-        {
-            Property = property;
-            OldValue = oldValue;
-            Description = description;
-            NewValue = newValue;
-            Root = root;
-        }
-
-        /// <summary>
-        ///     Creates new change for mixed reverse process based system with new value property based system
-        /// </summary>
-        /// <param name="property">Name of property, which new value will be applied to</param>
-        /// <param name="reverseProcess">Method with reversing value process</param>
-        /// <param name="reverseArguments">Arguments for reverse method</param>
-        /// <param name="newValue">New value of property</param>
-        /// <param name="description">Description of change</param>
-        /// <param name="root">Custom root for finding property</param>
-        public Change(string property, Action<object[]> reverseProcess, object[] reverseArguments,
-            object newValue, string description = "", object root = null)
-        {
-            Property = property;
-            ReverseProcess = reverseProcess;
-            ReverseProcessArguments = reverseArguments;
-            NewValue = newValue;
-            Description = description;
-            Root = root;
-        }
-
-        /// <summary>
-        ///     Creates new change for reverse process based system
-        /// </summary>
-        /// <param name="reverseProcess">Method with reversing value process</param>
-        /// <param name="reverseArguments">Arguments for reverse method</param>
-        /// <param name="process">Method with reversing the reversed value</param>
-        /// <param name="processArguments">Arguments for process method</param>
-        /// <param name="description">Description of change</param>
-        public Change(Action<object[]> reverseProcess, object[] reverseArguments,
-            Action<object[]> process, object[] processArguments, string description = "")
-        {
-            ReverseProcess = reverseProcess;
-            ReverseProcessArguments = reverseArguments;
-            Process = process;
-            ProcessArguments = processArguments;
-            Description = description;
-        }
-
-        public object OldValue { get; set; }
-
-        public object NewValue { get; set; }
-
-        public string Description { get; set; }
-
-        public string Property { get; set; }
-
-        public Action<object[]> ReverseProcess { get; set; }
+using System;
 
 
-        public Action<object[]> Process { get; set; }
+namespace PixiEditor.Models.DataHolders
+{
+    [Serializable]
+    public class Change
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Change"/> class.
+        ///     Creates new change for property based undo system.
+        /// </summary>
+        /// <param name="property">Name of property.</param>
+        /// <param name="oldValue">Old value of property.</param>
+        /// <param name="newValue">New value of property.</param>
+        /// <param name="description">Description of change.</param>
+        /// <param name="root">Custom root for finding property.</param>
+        public Change(
+            string property,
+            object oldValue,
+            object newValue,
+            string description = "",
+            object root = null)
+        {
+            Property = property;
+            OldValue = oldValue;
+            Description = description;
+            NewValue = newValue;
+            Root = root;
+        }
 
 
-        public object Root { get; set; }
-    }
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Change"/> class.
+        ///     Creates new change for mixed reverse process based system with new value property based system.
+        /// </summary>
+        /// <param name="property">Name of property, which new value will be applied to.</param>
+        /// <param name="reverseProcess">Method with reversing value process.</param>
+        /// <param name="reverseArguments">Arguments for reverse method.</param>
+        /// <param name="newValue">New value of property.</param>
+        /// <param name="description">Description of change.</param>
+        /// <param name="root">Custom root for finding property.</param>
+        public Change(
+            string property,
+            Action<object[]> reverseProcess,
+            object[] reverseArguments,
+            object newValue,
+            string description = "",
+            object root = null)
+        {
+            Property = property;
+            ReverseProcess = reverseProcess;
+            ReverseProcessArguments = reverseArguments;
+            NewValue = newValue;
+            Description = description;
+            Root = root;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Change"/> class.
+        ///     Creates new change for reverse process based system.
+        /// </summary>
+        /// <param name="reverseProcess">Method with reversing value process.</param>
+        /// <param name="reverseArguments">Arguments for reverse method.</param>
+        /// <param name="process">Method with reversing the reversed value.</param>
+        /// <param name="processArguments">Arguments for process method.</param>
+        /// <param name="description">Description of change.</param>
+        public Change(
+            Action<object[]> reverseProcess,
+            object[] reverseArguments,
+            Action<object[]> process,
+            object[] processArguments,
+            string description = "")
+        {
+            ReverseProcess = reverseProcess;
+            ReverseProcessArguments = reverseArguments;
+            Process = process;
+            ProcessArguments = processArguments;
+            Description = description;
+        }
+
+        public object[] ProcessArguments { get; set; }
+
+        public object[] ReverseProcessArguments { get; set; }
+
+        public object OldValue { get; set; }
+
+        public object NewValue { get; set; }
+
+        public string Description { get; set; }
+
+        public string Property { get; set; }
+
+        public Action<object[]> ReverseProcess { get; set; }
+
+        public Action<object[]> Process { get; set; }
+
+        public object Root { get; set; }
+    }
 }
 }

+ 284 - 284
PixiEditor/Models/DataHolders/Document.cs

@@ -1,102 +1,193 @@
-using System;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Windows;
-using System.Windows.Media;
-using PixiEditor.Helpers;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.Enums;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-
-namespace PixiEditor.Models.DataHolders
-{
-    public class Document : NotifyableObject
-    {
-        private int activeLayerIndex;
-        private int height;
-        private int width;
-
-        public Document(int width, int height)
-        {
-            Width = width;
-            Height = height;
-        }
-
-        public int Width
-        {
-            get => width;
-            set
-            {
-                width = value;
-                RaisePropertyChanged("Width");
-            }
-        }
-
-        public int Height
-        {
-            get => height;
-            set
-            {
-                height = value;
-                RaisePropertyChanged("Height");
-            }
-        }
-
-        public ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
-
-        public Layer ActiveLayer => Layers.Count > 0 ? Layers[ActiveLayerIndex] : null;
-
-        public int ActiveLayerIndex
-        {
-            get => activeLayerIndex;
-            set
-            {
-                activeLayerIndex = value;
-                RaisePropertyChanged("ActiveLayerIndex");
-                RaisePropertyChanged("ActiveLayer");
-            }
-        }
-
-        public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
-
-        public event EventHandler<DocumentSizeChangedEventArgs> DocumentSizeChanged;
-
-        /// <summary>
-        ///     Resizes canvas to specified width and height to selected anchor
-        /// </summary>
-        /// <param name="width">New width of canvas</param>
-        /// <param name="height">New height of canvas</param>
-        /// <param name="anchor">
-        ///     Point that will act as "starting position" of resizing. Use pipe to connect horizontal and
-        ///     vertical.
-        /// </param>
-        public void ResizeCanvas(int width, int height, AnchorPoint anchor)
-        {
-            int oldWidth = Width;
-            int oldHeight = Height;
-
-            int offsetX = GetOffsetXForAnchor(Width, width, anchor);
-            int offsetY = GetOffsetYForAnchor(Height, height, anchor);
-
-            Thickness[] oldOffsets = Layers.Select(x => x.Offset).ToArray();
-            Thickness[] newOffsets = Layers.Select(x => new Thickness(offsetX + x.OffsetX, offsetY + x.OffsetY, 0, 0))
-                .ToArray();
-
-            object[] processArgs = { newOffsets, width, height };
-            object[] reverseProcessArgs = { oldOffsets, Width, Height };
-
-            ResizeCanvas(newOffsets, width, height);
-            UndoManager.AddUndoChange(new Change(ResizeCanvasProcess,
-                reverseProcessArgs, ResizeCanvasProcess, processArgs, "Resize canvas"));
-            DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
-        }
-
-        private int GetOffsetXForAnchor(int srcWidth, int destWidth, AnchorPoint anchor)
-        {
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+
+namespace PixiEditor.Models.DataHolders
+{
+    public class Document : NotifyableObject
+    {
+        private int activeLayerIndex;
+        private int height;
+        private int width;
+
+        public Document(int width, int height)
+        {
+            Width = width;
+            Height = height;
+        }
+
+        public event EventHandler<DocumentSizeChangedEventArgs> DocumentSizeChanged;
+
+        public int Width
+        {
+            get => width;
+            set
+            {
+                width = value;
+                RaisePropertyChanged("Width");
+            }
+        }
+
+        public int Height
+        {
+            get => height;
+            set
+            {
+                height = value;
+                RaisePropertyChanged("Height");
+            }
+        }
+
+        public ObservableCollection<Layer> Layers { get; set; } = new ObservableCollection<Layer>();
+
+        public Layer ActiveLayer => Layers.Count > 0 ? Layers[ActiveLayerIndex] : null;
+
+        public int ActiveLayerIndex
+        {
+            get => activeLayerIndex;
+            set
+            {
+                activeLayerIndex = value;
+                RaisePropertyChanged("ActiveLayerIndex");
+                RaisePropertyChanged("ActiveLayer");
+            }
+        }
+
+        public ObservableCollection<Color> Swatches { get; set; } = new ObservableCollection<Color>();
+
+        /// <summary>
+        ///     Resizes canvas to specified width and height to selected anchor.
+        /// </summary>
+        /// <param name="width">New width of canvas.</param>
+        /// <param name="height">New height of canvas.</param>
+        /// <param name="anchor">
+        ///     Point that will act as "starting position" of resizing. Use pipe to connect horizontal and
+        ///     vertical.
+        /// </param>
+        public void ResizeCanvas(int width, int height, AnchorPoint anchor)
+        {
+            int oldWidth = Width;
+            int oldHeight = Height;
+
+            int offsetX = GetOffsetXForAnchor(Width, width, anchor);
+            int offsetY = GetOffsetYForAnchor(Height, height, anchor);
+
+            Thickness[] oldOffsets = Layers.Select(x => x.Offset).ToArray();
+            Thickness[] newOffsets = Layers.Select(x => new Thickness(offsetX + x.OffsetX, offsetY + x.OffsetY, 0, 0))
+                .ToArray();
+
+            object[] processArgs = { newOffsets, width, height };
+            object[] reverseProcessArgs = { oldOffsets, Width, Height };
+
+            ResizeCanvas(newOffsets, width, height);
+            UndoManager.AddUndoChange(new Change(
+                ResizeCanvasProcess,
+                reverseProcessArgs,
+                ResizeCanvasProcess,
+                processArgs,
+                "Resize canvas"));
+            DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
+        }
+
+        /// <summary>
+        ///     Resizes all document layers using NearestNeighbor interpolation.
+        /// </summary>
+        /// <param name="newWidth">New document width.</param>
+        /// <param name="newHeight">New document height.</param>
+        public void Resize(int newWidth, int newHeight)
+        {
+            object[] reverseArgs = { Width, Height };
+            object[] args = { newWidth, newHeight };
+            ResizeDocument(args);
+            UndoManager.AddUndoChange(new Change(
+                ResizeDocument,
+                reverseArgs,
+                ResizeDocument,
+                args,
+                "Resize document"));
+        }
+
+        /// <summary>
+        ///     Resizes canvas, so it fits exactly the size of drawn content, without any transparent pixels outside.
+        /// </summary>
+        public void ClipCanvas()
+        {
+            DoubleCords points = GetEdgePoints();
+            int smallestX = points.Coords1.X;
+            int smallestY = points.Coords1.Y;
+            int biggestX = points.Coords2.X;
+            int biggestY = points.Coords2.Y;
+
+            if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
+            {
+                return;
+            }
+
+            int width = biggestX - smallestX;
+            int height = biggestY - smallestY;
+            Coordinates moveVector = new Coordinates(-smallestX, -smallestY);
+
+            Thickness[] oldOffsets = Layers.Select(x => x.Offset).ToArray();
+            int oldWidth = Width;
+            int oldHeight = Height;
+
+            MoveOffsets(moveVector);
+            Width = width;
+            Height = height;
+
+            object[] reverseArguments = { oldOffsets, oldWidth, oldHeight };
+            object[] processArguments = { Layers.Select(x => x.Offset).ToArray(), width, height };
+
+            UndoManager.AddUndoChange(new Change(
+                ResizeCanvasProcess,
+                reverseArguments,
+                ResizeCanvasProcess,
+                processArguments,
+                "Clip canvas"));
+        }
+
+        public void CenterContent()
+        {
+            DoubleCords points = GetEdgePoints();
+
+            int smallestX = points.Coords1.X;
+            int smallestY = points.Coords1.Y;
+            int biggestX = points.Coords2.X;
+            int biggestY = points.Coords2.Y;
+
+            if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
+            {
+                return;
+            }
+
+            Coordinates contentCenter = CoordinatesCalculator.GetCenterPoint(points.Coords1, points.Coords2);
+            Coordinates documentCenter = CoordinatesCalculator.GetCenterPoint(
+                new Coordinates(0, 0),
+                new Coordinates(Width, Height));
+            Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
+
+            MoveOffsets(moveVector);
+            UndoManager.AddUndoChange(
+                new Change(
+                    MoveOffsetsProcess,
+                    new object[] { new Coordinates(-moveVector.X, -moveVector.Y) },
+                    MoveOffsetsProcess,
+                    new object[] { moveVector },
+                    "Center content"));
+        }
+
+        private int GetOffsetXForAnchor(int srcWidth, int destWidth, AnchorPoint anchor)
+        {
             if (anchor.HasFlag(AnchorPoint.Center))
             if (anchor.HasFlag(AnchorPoint.Center))
             {
             {
-                return Math.Abs(destWidth / 2 - srcWidth / 2);
+                return Math.Abs((destWidth / 2) - (srcWidth / 2));
             }
             }
 
 
             if (anchor.HasFlag(AnchorPoint.Right))
             if (anchor.HasFlag(AnchorPoint.Right))
@@ -104,14 +195,14 @@ namespace PixiEditor.Models.DataHolders
                 return Math.Abs(destWidth - srcWidth);
                 return Math.Abs(destWidth - srcWidth);
             }
             }
 
 
-            return 0;
-        }
-
-        private int GetOffsetYForAnchor(int srcHeight, int destHeight, AnchorPoint anchor)
-        {
+            return 0;
+        }
+
+        private int GetOffsetYForAnchor(int srcHeight, int destHeight, AnchorPoint anchor)
+        {
             if (anchor.HasFlag(AnchorPoint.Middle))
             if (anchor.HasFlag(AnchorPoint.Middle))
             {
             {
-                return Math.Abs(destHeight / 2 - srcHeight / 2);
+                return Math.Abs((destHeight / 2) - (srcHeight / 2));
             }
             }
 
 
             if (anchor.HasFlag(AnchorPoint.Bottom))
             if (anchor.HasFlag(AnchorPoint.Bottom))
@@ -119,125 +210,77 @@ namespace PixiEditor.Models.DataHolders
                 return Math.Abs(destHeight - srcHeight);
                 return Math.Abs(destHeight - srcHeight);
             }
             }
 
 
-            return 0;
-        }
-
-        /// <summary>
-        ///     Resizes all document layers using NearestNeighbor interpolation.
-        /// </summary>
-        /// <param name="newWidth">New document width</param>
-        /// <param name="newHeight">New document height</param>
-        public void Resize(int newWidth, int newHeight)
-        {
-            object[] reverseArgs = { Width, Height };
-            object[] args = { newWidth, newHeight };
-            ResizeDocument(args);
-            UndoManager.AddUndoChange(new Change(ResizeDocument, reverseArgs,
-                ResizeDocument, args, "Resize document"));
-        }
-
-        private void ResizeDocument(object[] arguments)
-        {
-            int oldWidth = Width;
-            int oldHeight = Height;
-
-            int newWidth = (int)arguments[0];
-            int newHeight = (int)arguments[1];
-
-            for (int i = 0; i < Layers.Count; i++)
-            {
-                float widthRatio = (float)newWidth / Width;
-                float heightRatio = (float)newHeight / Height;
-                int layerWidth = (int)(Layers[i].Width * widthRatio);
-                int layerHeight = (int)(Layers[i].Height * heightRatio);
-
-                Layers[i].Resize(layerWidth, layerHeight, newWidth, newHeight);
-                Layers[i].Offset = new Thickness(Layers[i].OffsetX * widthRatio, Layers[i].OffsetY * heightRatio, 0, 0);
-            }
-
-            Height = newHeight;
-            Width = newWidth;
-            DocumentSizeChanged?.Invoke(this,
-                new DocumentSizeChangedEventArgs(oldWidth, oldHeight, newWidth, newHeight));
-        }
-
-        private void ResizeCanvasProcess(object[] arguments)
-        {
-            int oldWidth = Width;
-            int oldHeight = Height;
-
-            Thickness[] offset = (Thickness[])arguments[0];
-            int width = (int)arguments[1];
-            int height = (int)arguments[2];
-            ResizeCanvas(offset, width, height);
-            DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
-        }
-
-        /// <summary>
-        ///     Resizes canvas
-        /// </summary>
-        /// <param name="offset">Offset of content in new canvas. It will move layer to that offset</param>
-        /// <param name="newWidth">New canvas size.</param>
-        /// <param name="newHeight">New canvas height.</param>
-        private void ResizeCanvas(Thickness[] offset, int newWidth, int newHeight)
-        {
-            for (int i = 0; i < Layers.Count; i++)
-            {
-                Layers[i].Offset = offset[i];
-                Layers[i].MaxWidth = newWidth;
-                Layers[i].MaxHeight = newHeight;
-            }
-
-            Width = newWidth;
-            Height = newHeight;
-        }
-
-        /// <summary>
-        ///     Resizes canvas, so it fits exactly the size of drawn content, without any transparent pixels outside.
-        /// </summary>
-        public void ClipCanvas()
-        {
-            DoubleCords points = GetEdgePoints();
-            int smallestX = points.Coords1.X;
-            int smallestY = points.Coords1.Y;
-            int biggestX = points.Coords2.X;
-            int biggestY = points.Coords2.Y;
-
-            if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
+            return 0;
+        }
+
+        private void ResizeDocument(object[] arguments)
+        {
+            int oldWidth = Width;
+            int oldHeight = Height;
+
+            int newWidth = (int)arguments[0];
+            int newHeight = (int)arguments[1];
+
+            for (int i = 0; i < Layers.Count; i++)
             {
             {
-                return;
+                float widthRatio = (float)newWidth / Width;
+                float heightRatio = (float)newHeight / Height;
+                int layerWidth = (int)(Layers[i].Width * widthRatio);
+                int layerHeight = (int)(Layers[i].Height * heightRatio);
+
+                Layers[i].Resize(layerWidth, layerHeight, newWidth, newHeight);
+                Layers[i].Offset = new Thickness(Layers[i].OffsetX * widthRatio, Layers[i].OffsetY * heightRatio, 0, 0);
+            }
+
+            Height = newHeight;
+            Width = newWidth;
+            DocumentSizeChanged?.Invoke(
+                this,
+                new DocumentSizeChangedEventArgs(oldWidth, oldHeight, newWidth, newHeight));
+        }
+
+        private void ResizeCanvasProcess(object[] arguments)
+        {
+            int oldWidth = Width;
+            int oldHeight = Height;
+
+            Thickness[] offset = (Thickness[])arguments[0];
+            int width = (int)arguments[1];
+            int height = (int)arguments[2];
+            ResizeCanvas(offset, width, height);
+            DocumentSizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(oldWidth, oldHeight, width, height));
+        }
+
+        /// <summary>
+        ///     Resizes canvas.
+        /// </summary>
+        /// <param name="offset">Offset of content in new canvas. It will move layer to that offset.</param>
+        /// <param name="newWidth">New canvas size.</param>
+        /// <param name="newHeight">New canvas height.</param>
+        private void ResizeCanvas(Thickness[] offset, int newWidth, int newHeight)
+        {
+            for (int i = 0; i < Layers.Count; i++)
+            {
+                Layers[i].Offset = offset[i];
+                Layers[i].MaxWidth = newWidth;
+                Layers[i].MaxHeight = newHeight;
             }
             }
 
 
-            int width = biggestX - smallestX;
-            int height = biggestY - smallestY;
-            Coordinates moveVector = new Coordinates(-smallestX, -smallestY);
-
-            Thickness[] oldOffsets = Layers.Select(x => x.Offset).ToArray();
-            int oldWidth = Width;
-            int oldHeight = Height;
-
-            MoveOffsets(moveVector);
-            Width = width;
-            Height = height;
-
-            object[] reverseArguments = { oldOffsets, oldWidth, oldHeight };
-            object[] processArguments = { Layers.Select(x => x.Offset).ToArray(), width, height };
-
-            UndoManager.AddUndoChange(new Change(ResizeCanvasProcess, reverseArguments,
-                ResizeCanvasProcess, processArguments, "Clip canvas"));
-        }
-
-        private DoubleCords GetEdgePoints()
-        {
-            Layer firstLayer = Layers[0];
-            int smallestX = firstLayer.OffsetX;
-            int smallestY = firstLayer.OffsetY;
-            int biggestX = smallestX + firstLayer.Width;
-            int biggestY = smallestY + firstLayer.Height;
-
-            for (int i = 0; i < Layers.Count; i++)
-            {
-                Layers[i].ClipCanvas();
+            Width = newWidth;
+            Height = newHeight;
+        }
+
+        private DoubleCords GetEdgePoints()
+        {
+            Layer firstLayer = Layers[0];
+            int smallestX = firstLayer.OffsetX;
+            int smallestY = firstLayer.OffsetY;
+            int biggestX = smallestX + firstLayer.Width;
+            int biggestY = smallestY + firstLayer.Height;
+
+            for (int i = 0; i < Layers.Count; i++)
+            {
+                Layers[i].ClipCanvas();
                 if (Layers[i].OffsetX < smallestX)
                 if (Layers[i].OffsetX < smallestX)
                 {
                 {
                     smallestX = Layers[i].OffsetX;
                     smallestX = Layers[i].OffsetX;
@@ -257,72 +300,29 @@ namespace PixiEditor.Models.DataHolders
                 {
                 {
                     biggestY = Layers[i].OffsetY + Layers[i].Height;
                     biggestY = Layers[i].OffsetY + Layers[i].Height;
                 }
                 }
-            }
-
-            return new DoubleCords(new Coordinates(smallestX, smallestY),
-                new Coordinates(biggestX, biggestY));
-        }
-
-        /// <summary>
-        ///     Moves offsets of layers by specified vector.
-        /// </summary>
-        /// <param name="moveVector"></param>
-        private void MoveOffsets(Coordinates moveVector)
-        {
-            for (int i = 0; i < Layers.Count; i++)
-            {
-                Thickness offset = Layers[i].Offset;
-                Layers[i].Offset = new Thickness(offset.Left + moveVector.X, offset.Top + moveVector.Y, 0, 0);
-            }
-        }
-
-        private void MoveOffsetsProcess(object[] arguments)
-        {
-            Coordinates vector = (Coordinates)arguments[0];
-            MoveOffsets(vector);
-        }
-
-        public void CenterContent()
-        {
-            DoubleCords points = GetEdgePoints();
-
-            int smallestX = points.Coords1.X;
-            int smallestY = points.Coords1.Y;
-            int biggestX = points.Coords2.X;
-            int biggestY = points.Coords2.Y;
-
-            if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
+            }
+
+            return new DoubleCords(
+                new Coordinates(smallestX, smallestY),
+                new Coordinates(biggestX, biggestY));
+        }
+
+        /// <summary>
+        ///     Moves offsets of layers by specified vector.
+        /// </summary>
+        private void MoveOffsets(Coordinates moveVector)
+        {
+            for (int i = 0; i < Layers.Count; i++)
             {
             {
-                return;
+                Thickness offset = Layers[i].Offset;
+                Layers[i].Offset = new Thickness(offset.Left + moveVector.X, offset.Top + moveVector.Y, 0, 0);
             }
             }
+        }
 
 
-            Coordinates contentCenter = CoordinatesCalculator.GetCenterPoint(points.Coords1, points.Coords2);
-            Coordinates documentCenter = CoordinatesCalculator.GetCenterPoint(new Coordinates(0, 0),
-                new Coordinates(Width, Height));
-            Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
-
-            MoveOffsets(moveVector);
-            UndoManager.AddUndoChange(new Change(MoveOffsetsProcess, new object[] { new Coordinates(-moveVector.X, -moveVector.Y) }, MoveOffsetsProcess,
-                new object[] { moveVector }, "Center content"));
-        }
-    }
-
-    public class DocumentSizeChangedEventArgs
-    {
-        public DocumentSizeChangedEventArgs(int oldWidth, int oldHeight, int newWidth, int newHeight)
-        {
-            OldWidth = oldWidth;
-            OldHeight = oldHeight;
-            NewWidth = newWidth;
-            NewHeight = newHeight;
-        }
-
-        public int OldWidth { get; set; }
-
-        public int OldHeight { get; set; }
-
-        public int NewWidth { get; set; }
-
-        public int NewHeight { get; set; }
-    }
+        private void MoveOffsetsProcess(object[] arguments)
+        {
+            Coordinates vector = (Coordinates)arguments[0];
+            MoveOffsets(vector);
+        }
+    }
 }
 }

+ 21 - 0
PixiEditor/Models/DataHolders/DocumentSizeChangedEventArgs.cs

@@ -0,0 +1,21 @@
+namespace PixiEditor.Models.DataHolders
+{
+    public class DocumentSizeChangedEventArgs
+    {
+        public DocumentSizeChangedEventArgs(int oldWidth, int oldHeight, int newWidth, int newHeight)
+        {
+            OldWidth = oldWidth;
+            OldHeight = oldHeight;
+            NewWidth = newWidth;
+            NewHeight = newHeight;
+        }
+
+        public int OldWidth { get; set; }
+
+        public int OldHeight { get; set; }
+
+        public int NewWidth { get; set; }
+
+        public int NewHeight { get; set; }
+    }
+}

+ 92 - 93
PixiEditor/Models/IO/Exporter.cs

@@ -1,98 +1,97 @@
-using System;
-using System.IO;
-using System.Windows;
-using System.Windows.Media.Imaging;
-using Microsoft.Win32;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Dialogs;
-
-namespace PixiEditor.Models.IO
-{
-    public class Exporter
-    {
-        public static Size FileDimensions;
-
-        public static string SaveDocumentPath { get; set; }
-
-        /// <summary>
-        ///     Saves document as .pixi file that contains all document data
-        /// </summary>
-        /// <param name="document">Document to save</param>
-        /// <param name="updateWorkspacePath">Should editor remember dialog path for further saves</param>
-        public static bool SaveAsEditableFileWithDialog(Document document, bool updateWorkspacePath = false)
-        {
-            SaveFileDialog dialog = new SaveFileDialog
-            {
-                Filter = "PixiEditor Files | *.pixi",
-                DefaultExt = "pixi"
-            };
-            if ((bool)dialog.ShowDialog())
-            {
-                SaveAsEditableFile(document, dialog.FileName, updateWorkspacePath);
-                return true;
-            }
-
-            return false;
-        }
-
-        public static void SaveAsEditableFile(Document document, string path, bool updateWorkspacePath = false)
-        {
-            BinarySerialization.WriteToBinaryFile(path, new SerializableDocument(document));
-
+using System;
+using System.IO;
+using System.Windows;
+using System.Windows.Media.Imaging;
+using Microsoft.Win32;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
+
+namespace PixiEditor.Models.IO
+{
+    public class Exporter
+    {
+        public static Size FileDimensions { get; set; }
+
+        public static string SaveDocumentPath { get; set; }
+
+        /// <summary>
+        ///     Saves document as .pixi file that contains all document data.
+        /// </summary>
+        /// <param name="document">Document to save.</param>
+        /// <param name="updateWorkspacePath">Should editor remember dialog path for further saves.</param>
+        public static bool SaveAsEditableFileWithDialog(Document document, bool updateWorkspacePath = false)
+        {
+            SaveFileDialog dialog = new SaveFileDialog
+            {
+                Filter = "PixiEditor Files | *.pixi",
+                DefaultExt = "pixi"
+            };
+            if ((bool)dialog.ShowDialog())
+            {
+                SaveAsEditableFile(document, dialog.FileName, updateWorkspacePath);
+                return true;
+            }
+
+            return false;
+        }
+
+        public static void SaveAsEditableFile(Document document, string path, bool updateWorkspacePath = false)
+        {
+            BinarySerialization.WriteToBinaryFile(path, new SerializableDocument(document));
+
             if (updateWorkspacePath)
             if (updateWorkspacePath)
             {
             {
                 SaveDocumentPath = path;
                 SaveDocumentPath = path;
             }
             }
-        }
-
-        /// <summary>
-        ///     Creates ExportFileDialog to get width, height and path of file.
-        /// </summary>
-        /// <param name="bitmap">Bitmap to be saved as file.</param>
-        /// <param name="fileDimensions">Size of file</param>
-        public static void Export(WriteableBitmap bitmap, Size fileDimensions)
-        {
-            ExportFileDialog info = new ExportFileDialog(fileDimensions);
-            // If OK on dialog has been clicked
-            if (info.ShowDialog())
-            {
-                // If sizes are incorrect
-                if (info.FileWidth < bitmap.Width || info.FileHeight < bitmap.Height)
-                {
-                    MessageBox.Show("Incorrect height or width value", "Error", MessageBoxButton.OK,
-                        MessageBoxImage.Error);
-                    return;
-                }
-
-                FileDimensions = new Size(info.FileWidth, info.FileHeight);
-                SaveAsPng(info.FilePath, info.FileWidth, info.FileHeight, bitmap);
-            }
-        }
-
-        /// <summary>
-        ///     Saves image to PNG file
-        /// </summary>
-        /// <param name="savePath">Save file path</param>
-        /// <param name="exportWidth">File width</param>
-        /// <param name="exportHeight">File height</param>
-        /// <param name="bitmap">Bitmap to save</param>
-        public static void SaveAsPng(string savePath, int exportWidth, int exportHeight, WriteableBitmap bitmap)
-        {
-            try
-            {
-                bitmap = bitmap.Resize(exportWidth, exportHeight,
-                    WriteableBitmapExtensions.Interpolation.NearestNeighbor);
-                using (FileStream stream = new FileStream(savePath, FileMode.Create))
-                {
-                    PngBitmapEncoder encoder = new PngBitmapEncoder();
-                    encoder.Frames.Add(BitmapFrame.Create(bitmap));
-                    encoder.Save(stream);
-                }
-            }
-            catch (Exception err)
-            {
-                MessageBox.Show(err.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
-            }
-        }
-    }
+        }
+
+        /// <summary>
+        ///     Creates ExportFileDialog to get width, height and path of file.
+        /// </summary>
+        /// <param name="bitmap">Bitmap to be saved as file.</param>
+        /// <param name="fileDimensions">Size of file.</param>
+        public static void Export(WriteableBitmap bitmap, Size fileDimensions)
+        {
+            ExportFileDialog info = new ExportFileDialog(fileDimensions);
+
+            // If OK on dialog has been clicked
+            if (info.ShowDialog())
+            {
+                // If sizes are incorrect
+                if (info.FileWidth < bitmap.Width || info.FileHeight < bitmap.Height)
+                {
+                    MessageBox.Show("Incorrect height or width value", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+                    return;
+                }
+
+                FileDimensions = new Size(info.FileWidth, info.FileHeight);
+                SaveAsPng(info.FilePath, info.FileWidth, info.FileHeight, bitmap);
+            }
+        }
+
+        /// <summary>
+        ///     Saves image to PNG file.
+        /// </summary>
+        /// <param name="savePath">Save file path.</param>
+        /// <param name="exportWidth">File width.</param>
+        /// <param name="exportHeight">File height.</param>
+        /// <param name="bitmap">Bitmap to save.</param>
+        public static void SaveAsPng(string savePath, int exportWidth, int exportHeight, WriteableBitmap bitmap)
+        {
+            try
+            {
+                bitmap = bitmap.Resize(exportWidth, exportHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+                using (FileStream stream = new FileStream(savePath, FileMode.Create))
+                {
+                    PngBitmapEncoder encoder = new PngBitmapEncoder();
+                    encoder.Frames.Add(BitmapFrame.Create(bitmap));
+                    encoder.Save(stream);
+                }
+            }
+            catch (Exception err)
+            {
+                MessageBox.Show(err.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+            }
+        }
+    }
 }
 }

+ 2 - 2
PixiEditor/Models/IO/Importer.cs

@@ -8,7 +8,7 @@ namespace PixiEditor.Models.IO
     public class Importer : NotifyableObject
     public class Importer : NotifyableObject
     {
     {
         /// <summary>
         /// <summary>
-        ///     Imports image from path and resizes it to given dimensions
+        ///     Imports image from path and resizes it to given dimensions.
         /// </summary>
         /// </summary>
         /// <param name="path">Path of image.</param>
         /// <param name="path">Path of image.</param>
         /// <param name="width">New width of image.</param>
         /// <param name="width">New width of image.</param>
@@ -26,7 +26,7 @@ namespace PixiEditor.Models.IO
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Imports image from path and resizes it to given dimensions
+        ///     Imports image from path and resizes it to given dimensions.
         /// </summary>
         /// </summary>
         /// <param name="path">Path of image.</param>
         /// <param name="path">Path of image.</param>
         public static WriteableBitmap ImportImage(string path)
         public static WriteableBitmap ImportImage(string path)

+ 65 - 66
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -1,41 +1,40 @@
-using System.Collections.Generic;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-
-namespace PixiEditor.Models.ImageManipulation
-{
-    public static class BitmapUtils
-    {
-        /// <summary>
-        ///     Converts pixel bytes to WriteableBitmap
-        /// </summary>
-        /// <param name="currentBitmapWidth">Width of bitmap</param>
-        /// <param name="currentBitmapHeight">Height of bitmap</param>
-        /// <param name="byteArray">Bitmap byte array</param>
-        /// <returns>WriteableBitmap</returns>
-        public static WriteableBitmap BytesToWriteableBitmap(int currentBitmapWidth, int currentBitmapHeight,
-            byte[] byteArray)
-        {
-            WriteableBitmap bitmap = BitmapFactory.New(currentBitmapWidth, currentBitmapHeight);
-            bitmap.FromByteArray(byteArray);
-            return bitmap;
-        }
-
-        /// <summary>
-        ///     Converts layers bitmaps into one bitmap.
-        /// </summary>
-        /// <param name="layers">Layers to combine</param>
-        /// <param name="width">Width of final bitmap</param>
-        /// <param name="height">Height of final bitmap</param>
-        /// <returns>WriteableBitmap of layered bitmaps</returns>
-        public static WriteableBitmap CombineLayers(Layer[] layers, int width, int height)
-        {
-            WriteableBitmap finalBitmap = BitmapFactory.New(width, height);
-
-            using (finalBitmap.GetBitmapContext())
-            {
+using System.Collections.Generic;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+
+namespace PixiEditor.Models.ImageManipulation
+{
+    public static class BitmapUtils
+    {
+        /// <summary>
+        ///     Converts pixel bytes to WriteableBitmap.
+        /// </summary>
+        /// <param name="currentBitmapWidth">Width of bitmap.</param>
+        /// <param name="currentBitmapHeight">Height of bitmap.</param>
+        /// <param name="byteArray">Bitmap byte array.</param>
+        /// <returns>WriteableBitmap.</returns>
+        public static WriteableBitmap BytesToWriteableBitmap(int currentBitmapWidth, int currentBitmapHeight, byte[] byteArray)
+        {
+            WriteableBitmap bitmap = BitmapFactory.New(currentBitmapWidth, currentBitmapHeight);
+            bitmap.FromByteArray(byteArray);
+            return bitmap;
+        }
+
+        /// <summary>
+        ///     Converts layers bitmaps into one bitmap.
+        /// </summary>
+        /// <param name="layers">Layers to combine.</param>
+        /// <param name="width">Width of final bitmap.</param>
+        /// <param name="height">Height of final bitmap.</param>
+        /// <returns>WriteableBitmap of layered bitmaps.</returns>
+        public static WriteableBitmap CombineLayers(Layer[] layers, int width, int height)
+        {
+            WriteableBitmap finalBitmap = BitmapFactory.New(width, height);
+
+            using (finalBitmap.GetBitmapContext())
+            {
                 for (int i = 0; i < layers.Length; i++)
                 for (int i = 0; i < layers.Length; i++)
                 {
                 {
                     for (int y = 0; y < finalBitmap.Height; y++)
                     for (int y = 0; y < finalBitmap.Height; y++)
@@ -51,38 +50,38 @@ namespace PixiEditor.Models.ImageManipulation
                         }
                         }
                     }
                     }
                 }
                 }
-            }
-
-            return finalBitmap;
-        }
-
-        public static Dictionary<Layer, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
-        {
-            Dictionary<Layer, Color[]> result = new Dictionary<Layer, Color[]>();
-
-            for (int i = 0; i < layers.Length; i++)
-            {
-                Color[] pixels = new Color[selection.Length];
-
-                using (layers[i].LayerBitmap.GetBitmapContext())
-                {
-                    for (int j = 0; j < pixels.Length; j++)
-                    {
-                        Coordinates position = layers[i].GetRelativePosition(selection[j]);
-                        if (position.X < 0 || position.X > layers[i].Width - 1 || position.Y < 0 ||
+            }
+
+            return finalBitmap;
+        }
+
+        public static Dictionary<Layer, Color[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
+        {
+            Dictionary<Layer, Color[]> result = new Dictionary<Layer, Color[]>();
+
+            for (int i = 0; i < layers.Length; i++)
+            {
+                Color[] pixels = new Color[selection.Length];
+
+                using (layers[i].LayerBitmap.GetBitmapContext())
+                {
+                    for (int j = 0; j < pixels.Length; j++)
+                    {
+                        Coordinates position = layers[i].GetRelativePosition(selection[j]);
+                        if (position.X < 0 || position.X > layers[i].Width - 1 || position.Y < 0 ||
                             position.Y > layers[i].Height - 1)
                             position.Y > layers[i].Height - 1)
                         {
                         {
                             continue;
                             continue;
                         }
                         }
 
 
-                        pixels[j] = layers[i].GetPixel(position.X, position.Y);
-                    }
-                }
+                        pixels[j] = layers[i].GetPixel(position.X, position.Y);
+                    }
+                }
+
+                result[layers[i]] = pixels;
+            }
 
 
-                result[layers[i]] = pixels;
-            }
-
-            return result;
-        }
-    }
+            return result;
+        }
+    }
 }
 }

+ 52 - 51
PixiEditor/Models/ImageManipulation/Morphology.cs

@@ -1,26 +1,26 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using PixiEditor.Models.Position;
-
-namespace PixiEditor.Models.ImageManipulation
-{
-    public class Morphology
-    {
-        public static IEnumerable<Coordinates> ApplyDilation(Coordinates[] points, int kernelSize, int[,] mask)
-        {
-            int kernelDim = kernelSize;
-
-            // This is the offset of center pixel from border of the kernel
-            int kernelOffset = (kernelDim - 1) / 2;
-            int margin = kernelDim;
-
-            byte[,] byteImg = GetByteArrayForPoints(points, margin);
-            byte[,] outputArray = byteImg.Clone() as byte[,];
-            Coordinates offset = new Coordinates(points.Min(x => x.X) - margin, points.Min(x => x.Y) - margin);
-
-            int width = byteImg.GetLength(0);
-            int height = byteImg.GetLength(1);
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using PixiEditor.Models.Position;
+
+namespace PixiEditor.Models.ImageManipulation
+{
+    public class Morphology
+    {
+        public static IEnumerable<Coordinates> ApplyDilation(Coordinates[] points, int kernelSize, int[,] mask)
+        {
+            int kernelDim = kernelSize;
+
+            // This is the offset of center pixel from border of the kernel
+            int kernelOffset = (kernelDim - 1) / 2;
+            int margin = kernelDim;
+
+            byte[,] byteImg = GetByteArrayForPoints(points, margin);
+            byte[,] outputArray = byteImg.Clone() as byte[,];
+            Coordinates offset = new Coordinates(points.Min(x => x.X) - margin, points.Min(x => x.Y) - margin);
+
+            int width = byteImg.GetLength(0);
+            int height = byteImg.GetLength(1);
             for (int y = kernelOffset; y < height - kernelOffset; y++)
             for (int y = kernelOffset; y < height - kernelOffset; y++)
             {
             {
                 for (int x = kernelOffset; x < width - kernelOffset; x++)
                 for (int x = kernelOffset; x < width - kernelOffset; x++)
@@ -42,19 +42,20 @@ namespace PixiEditor.Models.ImageManipulation
                             }
                             }
                         }
                         }
                     }
                     }
+
                     // Write processed data into the second array
                     // Write processed data into the second array
                     outputArray[x, y] = value;
                     outputArray[x, y] = value;
                 }
                 }
             }
             }
 
 
-            return ToCoordinates(outputArray, offset).Distinct();
-        }
-
-        private static IEnumerable<Coordinates> ToCoordinates(byte[,] byteArray, Coordinates offset)
-        {
-            List<Coordinates> output = new List<Coordinates>();
-            int width = byteArray.GetLength(0);
-
+            return ToCoordinates(outputArray, offset).Distinct();
+        }
+
+        private static IEnumerable<Coordinates> ToCoordinates(byte[,] byteArray, Coordinates offset)
+        {
+            List<Coordinates> output = new List<Coordinates>();
+            int width = byteArray.GetLength(0);
+
             for (int y = 0; y < byteArray.GetLength(1); y++)
             for (int y = 0; y < byteArray.GetLength(1); y++)
             {
             {
                 for (int x = 0; x < width; x++)
                 for (int x = 0; x < width; x++)
@@ -66,16 +67,16 @@ namespace PixiEditor.Models.ImageManipulation
                 }
                 }
             }
             }
 
 
-            return output;
-        }
-
-        private static byte[,] GetByteArrayForPoints(Coordinates[] points, int margin)
-        {
-            Tuple<int, int> dimensions = GetDimensionsForPoints(points);
-            int minX = points.Min(x => x.X);
-            int minY = points.Min(x => x.Y);
-            byte[,] array = new byte[dimensions.Item1 + margin * 2, dimensions.Item2 + margin * 2];
-
+            return output;
+        }
+
+        private static byte[,] GetByteArrayForPoints(Coordinates[] points, int margin)
+        {
+            Tuple<int, int> dimensions = GetDimensionsForPoints(points);
+            int minX = points.Min(x => x.X);
+            int minY = points.Min(x => x.Y);
+            byte[,] array = new byte[dimensions.Item1 + (margin * 2), dimensions.Item2 + (margin * 2)];
+
             for (int y = 0; y < dimensions.Item2 + margin; y++)
             for (int y = 0; y < dimensions.Item2 + margin; y++)
             {
             {
                 for (int x = 0; x < dimensions.Item1 + margin; x++)
                 for (int x = 0; x < dimensions.Item1 + margin; x++)
@@ -85,14 +86,14 @@ namespace PixiEditor.Models.ImageManipulation
                 }
                 }
             }
             }
 
 
-            return array;
-        }
-
-        private static Tuple<int, int> GetDimensionsForPoints(Coordinates[] points)
-        {
-            int width = points.Max(x => x.X) - points.Min(x => x.X);
-            int height = points.Max(x => x.Y) - points.Min(x => x.Y);
-            return new Tuple<int, int>(width + 1, height + 1);
-        }
-    }
+            return array;
+        }
+
+        private static Tuple<int, int> GetDimensionsForPoints(Coordinates[] points)
+        {
+            int width = points.Max(x => x.X) - points.Min(x => x.X);
+            int height = points.Max(x => x.Y) - points.Min(x => x.Y);
+            return new Tuple<int, int>(width + 1, height + 1);
+        }
+    }
 }
 }

+ 3 - 3
PixiEditor/Models/ImageManipulation/Transform.cs

@@ -7,9 +7,9 @@ namespace PixiEditor.Models.ImageManipulation
         /// <summary>
         /// <summary>
         ///     Returns translation between two coordinates.
         ///     Returns translation between two coordinates.
         /// </summary>
         /// </summary>
-        /// <param name="from">Starting coordinate</param>
-        /// <param name="to">New coordinate</param>
-        /// <returns>Translation as coordinate</returns>
+        /// <param name="from">Starting coordinate.</param>
+        /// <param name="to">New coordinate.</param>
+        /// <returns>Translation as coordinate.</returns>
         public static Coordinates GetTranslation(Coordinates from, Coordinates to)
         public static Coordinates GetTranslation(Coordinates from, Coordinates to)
         {
         {
             int translationX = to.X - from.X;
             int translationX = to.X - from.X;

+ 415 - 422
PixiEditor/Models/Layers/Layer.cs

@@ -1,227 +1,224 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Position;
-
-namespace PixiEditor.Models.Layers
-{
-    public class Layer : BasicLayer
-    {
-        private const int SizeOfArgb = 4;
-        private bool clipRequested;
-
-        private bool isActive;
-
-        private bool isRenaming;
-        private bool isVisible = true;
-        private WriteableBitmap layerBitmap;
-
-        private string name;
-
-        private Thickness offset;
-
-        private float opacity = 1;
-
-        public Dictionary<Coordinates, Color> LastRelativeCoordinates;
-
-        public Layer(string name)
-        {
-            Name = name;
-            LayerBitmap = BitmapFactory.New(0, 0);
-            Width = 0;
-            Height = 0;
-        }
-
-        public Layer(string name, int width, int height)
-        {
-            Name = name;
-            LayerBitmap = BitmapFactory.New(width, height);
-            Width = width;
-            Height = height;
-        }
-
-        public Layer(string name, WriteableBitmap layerBitmap)
-        {
-            Name = name;
-            LayerBitmap = layerBitmap;
-            Width = layerBitmap.PixelWidth;
-            Height = layerBitmap.PixelHeight;
-        }
-
-        public string Name
-        {
-            get => name;
-            set
-            {
-                name = value;
-                RaisePropertyChanged("Name");
-            }
-        }
-
-        public bool IsActive
-        {
-            get => isActive;
-            set
-            {
-                isActive = value;
-                RaisePropertyChanged("IsActive");
-            }
-        }
-
-        public bool IsVisible
-        {
-            get => isVisible;
-            set
-            {
-                isVisible = value;
-                RaisePropertyChanged("IsVisible");
-            }
-        }
-
-        public bool IsRenaming
-        {
-            get => isRenaming;
-            set
-            {
-                isRenaming = value;
-                RaisePropertyChanged("IsRenaming");
-            }
-        }
-
-        public WriteableBitmap LayerBitmap
-        {
-            get => layerBitmap;
-            set
-            {
-                layerBitmap = value;
-                RaisePropertyChanged("LayerBitmap");
-            }
-        }
-
-        public float Opacity
-        {
-            get => opacity;
-            set
-            {
-                opacity = value;
-                RaisePropertyChanged("Opacity");
-            }
-        }
-
-        public int OffsetX => (int)Offset.Left;
-
-        public int OffsetY => (int)Offset.Top;
-
-        public Thickness Offset
-        {
-            get => offset;
-            set
-            {
-                offset = value;
-                RaisePropertyChanged("Offset");
-            }
-        }
-
-        public int MaxWidth { get; set; } = int.MaxValue;
-
-        public int MaxHeight { get; set; } = int.MaxValue;
-
-        /// <summary>
-        ///     Returns clone of layer
-        /// </summary>
-        /// <returns></returns>
-        public Layer Clone()
-        {
-            return new Layer(Name, LayerBitmap.Clone())
-            {
-                IsVisible = IsVisible,
-                Offset = Offset,
-                MaxHeight = MaxHeight,
-                MaxWidth = MaxWidth,
-                Opacity = Opacity,
-                IsActive = IsActive,
-                IsRenaming = IsRenaming
-            };
-        }
-
-        /// <summary>
-        ///     Resizes bitmap with it's content using NearestNeighbor interpolation
-        /// </summary>
-        /// <param name="width">New width</param>
-        /// <param name="height">New height</param>
-        /// <param name="newMaxWidth">New layer maximum width, this should be document width</param>
-        /// <param name="newMaxHeight">New layer maximum height, this should be document height</param>
-        public void Resize(int width, int height, int newMaxWidth, int newMaxHeight)
-        {
-            LayerBitmap = LayerBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
-            Width = width;
-            Height = height;
-            MaxWidth = newMaxWidth;
-            MaxHeight = newMaxHeight;
-        }
-
-        /// <summary>
-        ///     Converts coordinates relative to viewport to relative to layer
-        /// </summary>
-        /// <param name="cords"></param>
-        /// <returns></returns>
-        public Coordinates GetRelativePosition(Coordinates cords)
-        {
-            return new Coordinates(cords.X - OffsetX, cords.Y - OffsetY);
-        }
-
-        /// <summary>
-        ///     Returns pixel color of x and y coordinates relative to document using (x - OffsetX) formula.
-        /// </summary>
-        /// <param name="x">Viewport relative X</param>
-        /// <param name="y">Viewport relative Y</param>
-        /// <returns>Color of a pixel</returns>
-        public Color GetPixelWithOffset(int x, int y)
-        {
-            Coordinates cords = GetRelativePosition(new Coordinates(x, y));
-            return GetPixel(cords.X, cords.Y);
-        }
-
-        /// <summary>
-        ///     Returns pixel color on x and y.
-        /// </summary>
-        /// <param name="x">X coordinate</param>
-        /// <param name="y">Y Coordinate</param>
-        /// <returns>Color of pixel, if out of bounds, returns transparent pixel.</returns>
-        public Color GetPixel(int x, int y)
-        {
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Position;
+
+namespace PixiEditor.Models.Layers
+{
+    public class Layer : BasicLayer
+    {
+        private const int SizeOfArgb = 4;
+        private bool clipRequested;
+
+        private bool isActive;
+
+        private bool isRenaming;
+        private bool isVisible = true;
+        private WriteableBitmap layerBitmap;
+
+        private string name;
+
+        private Thickness offset;
+
+        private float opacity = 1;
+
+        public Layer(string name)
+        {
+            Name = name;
+            LayerBitmap = BitmapFactory.New(0, 0);
+            Width = 0;
+            Height = 0;
+        }
+
+        public Layer(string name, int width, int height)
+        {
+            Name = name;
+            LayerBitmap = BitmapFactory.New(width, height);
+            Width = width;
+            Height = height;
+        }
+
+        public Layer(string name, WriteableBitmap layerBitmap)
+        {
+            Name = name;
+            LayerBitmap = layerBitmap;
+            Width = layerBitmap.PixelWidth;
+            Height = layerBitmap.PixelHeight;
+        }
+
+        public Dictionary<Coordinates, Color> LastRelativeCoordinates { get; set; }
+
+        public string Name
+        {
+            get => name;
+            set
+            {
+                name = value;
+                RaisePropertyChanged("Name");
+            }
+        }
+
+        public bool IsActive
+        {
+            get => isActive;
+            set
+            {
+                isActive = value;
+                RaisePropertyChanged("IsActive");
+            }
+        }
+
+        public bool IsVisible
+        {
+            get => isVisible;
+            set
+            {
+                isVisible = value;
+                RaisePropertyChanged("IsVisible");
+            }
+        }
+
+        public bool IsRenaming
+        {
+            get => isRenaming;
+            set
+            {
+                isRenaming = value;
+                RaisePropertyChanged("IsRenaming");
+            }
+        }
+
+        public WriteableBitmap LayerBitmap
+        {
+            get => layerBitmap;
+            set
+            {
+                layerBitmap = value;
+                RaisePropertyChanged("LayerBitmap");
+            }
+        }
+
+        public float Opacity
+        {
+            get => opacity;
+            set
+            {
+                opacity = value;
+                RaisePropertyChanged("Opacity");
+            }
+        }
+
+        public int OffsetX => (int)Offset.Left;
+
+        public int OffsetY => (int)Offset.Top;
+
+        public Thickness Offset
+        {
+            get => offset;
+            set
+            {
+                offset = value;
+                RaisePropertyChanged("Offset");
+            }
+        }
+
+        public int MaxWidth { get; set; } = int.MaxValue;
+
+        public int MaxHeight { get; set; } = int.MaxValue;
+
+        /// <summary>
+        ///     Returns clone of layer.
+        /// </summary>
+        public Layer Clone()
+        {
+            return new Layer(Name, LayerBitmap.Clone())
+            {
+                IsVisible = IsVisible,
+                Offset = Offset,
+                MaxHeight = MaxHeight,
+                MaxWidth = MaxWidth,
+                Opacity = Opacity,
+                IsActive = IsActive,
+                IsRenaming = IsRenaming
+            };
+        }
+
+        /// <summary>
+        ///     Resizes bitmap with it's content using NearestNeighbor interpolation.
+        /// </summary>
+        /// <param name="width">New width.</param>
+        /// <param name="height">New height.</param>
+        /// <param name="newMaxWidth">New layer maximum width, this should be document width.</param>
+        /// <param name="newMaxHeight">New layer maximum height, this should be document height.</param>
+        public void Resize(int width, int height, int newMaxWidth, int newMaxHeight)
+        {
+            LayerBitmap = LayerBitmap.Resize(width, height, WriteableBitmapExtensions.Interpolation.NearestNeighbor);
+            Width = width;
+            Height = height;
+            MaxWidth = newMaxWidth;
+            MaxHeight = newMaxHeight;
+        }
+
+        /// <summary>
+        ///     Converts coordinates relative to viewport to relative to layer.
+        /// </summary>
+        public Coordinates GetRelativePosition(Coordinates cords)
+        {
+            return new Coordinates(cords.X - OffsetX, cords.Y - OffsetY);
+        }
+
+        /// <summary>
+        ///     Returns pixel color of x and y coordinates relative to document using (x - OffsetX) formula.
+        /// </summary>
+        /// <param name="x">Viewport relative X.</param>
+        /// <param name="y">Viewport relative Y.</param>
+        /// <returns>Color of a pixel.</returns>
+        public Color GetPixelWithOffset(int x, int y)
+        {
+            Coordinates cords = GetRelativePosition(new Coordinates(x, y));
+            return GetPixel(cords.X, cords.Y);
+        }
+
+        /// <summary>
+        ///     Returns pixel color on x and y.
+        /// </summary>
+        /// <param name="x">X coordinate.</param>
+        /// <param name="y">Y Coordinate.</param>
+        /// <returns>Color of pixel, if out of bounds, returns transparent pixel.</returns>
+        public Color GetPixel(int x, int y)
+        {
             if (x > Width - 1 || x < 0 || y > Height - 1 || y < 0)
             if (x > Width - 1 || x < 0 || y > Height - 1 || y < 0)
             {
             {
                 return Color.FromArgb(0, 0, 0, 0);
                 return Color.FromArgb(0, 0, 0, 0);
             }
             }
 
 
-            return LayerBitmap.GetPixel(x, y);
-        }
-
-        /// <summary>
-        ///     Applies pixel to layer
-        /// </summary>
-        /// <param name="coordinates">Position of pixel</param>
-        /// <param name="color">Color of pixel</param>
-        /// <param name="dynamicResize">Resizes bitmap to fit content</param>
-        /// <param name="applyOffset">Converts pixels coordinates to relative to bitmap</param>
-        public void SetPixel(Coordinates coordinates, Color color, bool dynamicResize = true, bool applyOffset = true)
-        {
-            SetPixels(BitmapPixelChanges.FromSingleColoredArray(new[] { coordinates }, color), dynamicResize, applyOffset);
-        }
-
-        /// <summary>
-        ///     Applies pixels to layer
-        /// </summary>
-        /// <param name="pixels">Pixels to apply</param>
-        /// <param name="dynamicResize">Resizes bitmap to fit content</param>
-        /// <param name="applyOffset">Converts pixels coordinates to relative to bitmap</param>
-        public void SetPixels(BitmapPixelChanges pixels, bool dynamicResize = true, bool applyOffset = true)
-        {
+            return LayerBitmap.GetPixel(x, y);
+        }
+
+        /// <summary>
+        ///     Applies pixel to layer.
+        /// </summary>
+        /// <param name="coordinates">Position of pixel.</param>
+        /// <param name="color">Color of pixel.</param>
+        /// <param name="dynamicResize">Resizes bitmap to fit content.</param>
+        /// <param name="applyOffset">Converts pixels coordinates to relative to bitmap.</param>
+        public void SetPixel(Coordinates coordinates, Color color, bool dynamicResize = true, bool applyOffset = true)
+        {
+            SetPixels(BitmapPixelChanges.FromSingleColoredArray(new[] { coordinates }, color), dynamicResize, applyOffset);
+        }
+
+        /// <summary>
+        ///     Applies pixels to layer.
+        /// </summary>
+        /// <param name="pixels">Pixels to apply.</param>
+        /// <param name="dynamicResize">Resizes bitmap to fit content.</param>
+        /// <param name="applyOffset">Converts pixels coordinates to relative to bitmap.</param>
+        public void SetPixels(BitmapPixelChanges pixels, bool dynamicResize = true, bool applyOffset = true)
+        {
             if (pixels.ChangedPixels == null || pixels.ChangedPixels.Count == 0)
             if (pixels.ChangedPixels == null || pixels.ChangedPixels.Count == 0)
             {
             {
                 return;
                 return;
@@ -237,67 +234,59 @@ namespace PixiEditor.Models.Layers
                 pixels.ChangedPixels = GetRelativePosition(pixels.ChangedPixels);
                 pixels.ChangedPixels = GetRelativePosition(pixels.ChangedPixels);
             }
             }
 
 
-            LastRelativeCoordinates = pixels.ChangedPixels;
-
-            using (BitmapContext ctx = LayerBitmap.GetBitmapContext())
-            {
-                foreach (KeyValuePair<Coordinates, Color> coords in pixels.ChangedPixels)
-                {
+            LastRelativeCoordinates = pixels.ChangedPixels;
+
+            using (BitmapContext ctx = LayerBitmap.GetBitmapContext())
+            {
+                foreach (KeyValuePair<Coordinates, Color> coords in pixels.ChangedPixels)
+                {
                     if (OutOfBounds(coords.Key))
                     if (OutOfBounds(coords.Key))
                     {
                     {
                         continue;
                         continue;
                     }
                     }
 
 
-                    ctx.WriteableBitmap.SetPixel(coords.Key.X, coords.Key.Y, coords.Value);
-                }
-            }
-
-            ClipIfNecessary();
-        }
-
-        private Dictionary<Coordinates, Color> GetRelativePosition(Dictionary<Coordinates, Color> changedPixels)
-        {
-            return changedPixels.ToDictionary(d => new Coordinates(d.Key.X - OffsetX, d.Key.Y - OffsetY),
-                d => d.Value);
-        }
-
-        /// <summary>
-        ///     Converts absolute coordinates array to relative to this layer coordinates array.
-        /// </summary>
-        /// <param name="nonRelativeCords">absolute coordinates array</param>
-        /// <returns></returns>
-        public Coordinates[] ConvertToRelativeCoordinates(Coordinates[] nonRelativeCords)
-        {
-            Coordinates[] result = new Coordinates[nonRelativeCords.Length];
+                    ctx.WriteableBitmap.SetPixel(coords.Key.X, coords.Key.Y, coords.Value);
+                }
+            }
+
+            ClipIfNecessary();
+        }
+
+        /// <summary>
+        ///     Converts absolute coordinates array to relative to this layer coordinates array.
+        /// </summary>
+        /// <param name="nonRelativeCords">absolute coordinates array.</param>
+        public Coordinates[] ConvertToRelativeCoordinates(Coordinates[] nonRelativeCords)
+        {
+            Coordinates[] result = new Coordinates[nonRelativeCords.Length];
             for (int i = 0; i < nonRelativeCords.Length; i++)
             for (int i = 0; i < nonRelativeCords.Length; i++)
             {
             {
                 result[i] = new Coordinates(nonRelativeCords[i].X - OffsetX, nonRelativeCords[i].Y - OffsetY);
                 result[i] = new Coordinates(nonRelativeCords[i].X - OffsetX, nonRelativeCords[i].Y - OffsetY);
             }
             }
 
 
-            return result;
-        }
-
-        /// <summary>
-        ///     Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth
-        /// </summary>
-        /// <param name="pixels"></param>
-        public void DynamicResize(BitmapPixelChanges pixels)
-        {
+            return result;
+        }
+
+        /// <summary>
+        ///     Resizes canvas to fit pixels outside current bounds. Clamped to MaxHeight and MaxWidth.
+        /// </summary>
+        public void DynamicResize(BitmapPixelChanges pixels)
+        {
             if (pixels.ChangedPixels.Count == 0)
             if (pixels.ChangedPixels.Count == 0)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            ResetOffset(pixels);
-            Tuple<DoubleCords, bool> borderData = ExtractBorderData(pixels);
-            DoubleCords minMaxCords = borderData.Item1;
-            int newMaxX = minMaxCords.Coords2.X - OffsetX;
-            int newMaxY = minMaxCords.Coords2.Y - OffsetY;
-            int newMinX = minMaxCords.Coords1.X - OffsetX;
-            int newMinY = minMaxCords.Coords1.Y - OffsetY;
-
-            if (!(pixels.WasBuiltAsSingleColored && pixels.ChangedPixels.First().Value.A == 0))
-            {
+            ResetOffset(pixels);
+            Tuple<DoubleCords, bool> borderData = ExtractBorderData(pixels);
+            DoubleCords minMaxCords = borderData.Item1;
+            int newMaxX = minMaxCords.Coords2.X - OffsetX;
+            int newMaxY = minMaxCords.Coords2.Y - OffsetY;
+            int newMinX = minMaxCords.Coords1.X - OffsetX;
+            int newMinY = minMaxCords.Coords1.Y - OffsetY;
+
+            if (!(pixels.WasBuiltAsSingleColored && pixels.ChangedPixels.First().Value.A == 0))
+            {
                 if (newMaxX + 1 > Width || newMaxY + 1 > Height)
                 if (newMaxX + 1 > Width || newMaxY + 1 > Height)
                 {
                 {
                     IncreaseSizeToBottom(newMaxX, newMaxY);
                     IncreaseSizeToBottom(newMaxX, newMaxY);
@@ -307,25 +296,74 @@ namespace PixiEditor.Models.Layers
                 {
                 {
                     IncreaseSizeToTop(newMinX, newMinY);
                     IncreaseSizeToTop(newMinX, newMinY);
                 }
                 }
-            }
-
-            if (borderData.Item2) // if clip is requested
+            }
+
+            // if clip is requested
+            if (borderData.Item2)
             {
             {
                 clipRequested = true;
                 clipRequested = true;
             }
             }
-        }
-
-        private Tuple<DoubleCords, bool> ExtractBorderData(BitmapPixelChanges pixels)
-        {
-            Coordinates firstCords = pixels.ChangedPixels.First().Key;
-            int minX = firstCords.X;
-            int minY = firstCords.Y;
-            int maxX = minX;
-            int maxY = minY;
-            bool clipRequested = false;
-
-            foreach (KeyValuePair<Coordinates, Color> pixel in pixels.ChangedPixels)
-            {
+        }
+
+        /// <summary>
+        ///     Changes size of bitmap to fit content.
+        /// </summary>
+        public void ClipCanvas()
+        {
+            DoubleCords points = GetEdgePoints();
+            int smallestX = points.Coords1.X;
+            int smallestY = points.Coords1.Y;
+            int biggestX = points.Coords2.X;
+            int biggestY = points.Coords2.Y;
+
+            if (smallestX < 0 && smallestY < 0 && biggestX < 0 && biggestY < 0)
+            {
+                return;
+            }
+
+            int width = biggestX - smallestX + 1;
+            int height = biggestY - smallestY + 1;
+            ResizeCanvas(0, 0, smallestX, smallestY, width, height);
+            Offset = new Thickness(OffsetX + smallestX, OffsetY + smallestY, 0, 0);
+        }
+
+        /// <summary>
+        ///     Clears bitmap.
+        /// </summary>
+        public void Clear()
+        {
+            LayerBitmap.Clear();
+        }
+
+        /// <summary>
+        ///     Converts layer WriteableBitmap to byte array.
+        /// </summary>
+        public byte[] ConvertBitmapToBytes()
+        {
+            LayerBitmap.Lock();
+            byte[] byteArray = LayerBitmap.ToByteArray();
+            LayerBitmap.Unlock();
+            return byteArray;
+        }
+
+        private Dictionary<Coordinates, Color> GetRelativePosition(Dictionary<Coordinates, Color> changedPixels)
+        {
+            return changedPixels.ToDictionary(
+                d => new Coordinates(d.Key.X - OffsetX, d.Key.Y - OffsetY),
+                d => d.Value);
+        }
+
+        private Tuple<DoubleCords, bool> ExtractBorderData(BitmapPixelChanges pixels)
+        {
+            Coordinates firstCords = pixels.ChangedPixels.First().Key;
+            int minX = firstCords.X;
+            int minY = firstCords.Y;
+            int maxX = minX;
+            int maxY = minY;
+            bool clipRequested = false;
+
+            foreach (KeyValuePair<Coordinates, Color> pixel in pixels.ChangedPixels)
+            {
                 if (pixel.Key.X < minX)
                 if (pixel.Key.X < minX)
                 {
                 {
                     minX = pixel.Key.X;
                     minX = pixel.Key.X;
@@ -348,153 +386,108 @@ namespace PixiEditor.Models.Layers
                 {
                 {
                     clipRequested = true;
                     clipRequested = true;
                 }
                 }
-            }
-
-            return new Tuple<DoubleCords, bool>(
-                new DoubleCords(new Coordinates(minX, minY), new Coordinates(maxX, maxY)), clipRequested);
-        }
-
-        private bool IsBorderPixel(Coordinates cords)
-        {
-            return cords.X - OffsetX == 0 || cords.Y - OffsetY == 0 || cords.X - OffsetX == Width - 1 ||
-                   cords.Y - OffsetY == Height - 1;
-        }
-
-        private bool OutOfBounds(Coordinates cords)
-        {
-            return cords.X < 0 || cords.X > Width - 1 || cords.Y < 0 || cords.Y > Height - 1;
-        }
-
-        private void ClipIfNecessary()
-        {
-            if (clipRequested)
-            {
-                ClipCanvas();
-                clipRequested = false;
-            }
-        }
-
-        /// <summary>
-        ///     Changes size of bitmap to fit content
-        /// </summary>
-        public void ClipCanvas()
-        {
-            DoubleCords points = GetEdgePoints();
-            int smallestX = points.Coords1.X;
-            int smallestY = points.Coords1.Y;
-            int biggestX = points.Coords2.X;
-            int biggestY = points.Coords2.Y;
-
-            if (smallestX < 0 && smallestY < 0 && biggestX < 0 && biggestY < 0)
+            }
+
+            return new Tuple<DoubleCords, bool>(
+                new DoubleCords(new Coordinates(minX, minY), new Coordinates(maxX, maxY)), clipRequested);
+        }
+
+        private bool IsBorderPixel(Coordinates cords)
+        {
+            return cords.X - OffsetX == 0 || cords.Y - OffsetY == 0 || cords.X - OffsetX == Width - 1 ||
+                   cords.Y - OffsetY == Height - 1;
+        }
+
+        private bool OutOfBounds(Coordinates cords)
+        {
+            return cords.X < 0 || cords.X > Width - 1 || cords.Y < 0 || cords.Y > Height - 1;
+        }
+
+        private void ClipIfNecessary()
+        {
+            if (clipRequested)
             {
             {
-                return;
+                ClipCanvas();
+                clipRequested = false;
             }
             }
+        }
 
 
-            int width = biggestX - smallestX + 1;
-            int height = biggestY - smallestY + 1;
-            ResizeCanvas(0, 0, smallestX, smallestY, width, height);
-            Offset = new Thickness(OffsetX + smallestX, OffsetY + smallestY, 0, 0);
-        }
-
-        private void IncreaseSizeToBottom(int newMaxX, int newMaxY)
-        {
+        private void IncreaseSizeToBottom(int newMaxX, int newMaxY)
+        {
             if (MaxWidth - OffsetX < 0 || MaxHeight - OffsetY < 0)
             if (MaxWidth - OffsetX < 0 || MaxHeight - OffsetY < 0)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            newMaxX = Math.Clamp(Math.Max(newMaxX + 1, Width), 0, MaxWidth - OffsetX);
-            newMaxY = Math.Clamp(Math.Max(newMaxY + 1, Height), 0, MaxHeight - OffsetY);
-
-            ResizeCanvas(0, 0, 0, 0, newMaxX, newMaxY);
-        }
-
-        private void IncreaseSizeToTop(int newMinX, int newMinY)
-        {
-            newMinX = Math.Clamp(Math.Min(newMinX, Width), Math.Min(-OffsetX, OffsetX), 0);
-            newMinY = Math.Clamp(Math.Min(newMinY, Height), Math.Min(-OffsetY, OffsetY), 0);
-
-            Offset = new Thickness(Math.Clamp(OffsetX + newMinX, 0, MaxWidth),
-                Math.Clamp(OffsetY + newMinY, 0, MaxHeight), 0, 0);
-
-            int newWidth = Math.Clamp(Width - newMinX, 0, MaxWidth);
-            int newHeight = Math.Clamp(Height - newMinY, 0, MaxHeight);
-
-            int offsetX = Math.Abs(newWidth - Width);
-            int offsetY = Math.Abs(newHeight - Height);
-
-            ResizeCanvas(offsetX, offsetY, 0, 0, newWidth, newHeight);
-        }
-
-        private DoubleCords GetEdgePoints()
-        {
-            Coordinates smallestPixel = CoordinatesCalculator.FindMinEdgeNonTransparentPixel(LayerBitmap);
-            Coordinates biggestPixel = CoordinatesCalculator.FindMostEdgeNonTransparentPixel(LayerBitmap);
-
-            return new DoubleCords(smallestPixel, biggestPixel);
-        }
-
-        private void ResetOffset(BitmapPixelChanges pixels)
-        {
-            if (Width == 0 || Height == 0)
-            {
-                int offsetX = pixels.ChangedPixels.Min(x => x.Key.X);
-                int offsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
-                Offset = new Thickness(offsetX, offsetY, 0, 0);
-            }
-        }
-
-        /// <summary>
-        ///     Clears bitmap
-        /// </summary>
-        public void Clear()
-        {
-            LayerBitmap.Clear();
-        }
-
-        /// <summary>
-        ///     Converts layer WriteableBitmap to byte array
-        /// </summary>
-        /// <returns></returns>
-        public byte[] ConvertBitmapToBytes()
-        {
-            LayerBitmap.Lock();
-            byte[] byteArray = LayerBitmap.ToByteArray();
-            LayerBitmap.Unlock();
-            return byteArray;
-        }
-
-        /// <summary>
-        ///     Resizes canvas to new size with specified offset.
-        /// </summary>
-        /// <param name="offsetX"></param>
-        /// <param name="offsetY"></param>
-        /// <param name="offsetXSrc"></param>
-        /// <param name="offsetYSrc"></param>
-        /// <param name="newWidth"></param>
-        /// <param name="newHeight"></param>
-        private void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int newWidth, int newHeight)
-        {
-            int iteratorHeight = Height > newHeight ? newHeight : Height;
-            int count = Width > newWidth ? newWidth : Width;
-
-            using (BitmapContext srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
-            {
-                WriteableBitmap result = BitmapFactory.New(newWidth, newHeight);
-                using (BitmapContext destContext = result.GetBitmapContext())
-                {
-                    for (int line = 0; line < iteratorHeight; line++)
-                    {
-                        int srcOff = ((offsetYSrc + line) * Width + offsetXSrc) * SizeOfArgb;
-                        int dstOff = ((offsetY + line) * newWidth + offsetX) * SizeOfArgb;
-                        BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * SizeOfArgb);
-                    }
-
-                    LayerBitmap = result;
-                    Width = newWidth;
-                    Height = newHeight;
-                }
-            }
-        }
-    }
+            newMaxX = Math.Clamp(Math.Max(newMaxX + 1, Width), 0, MaxWidth - OffsetX);
+            newMaxY = Math.Clamp(Math.Max(newMaxY + 1, Height), 0, MaxHeight - OffsetY);
+
+            ResizeCanvas(0, 0, 0, 0, newMaxX, newMaxY);
+        }
+
+        private void IncreaseSizeToTop(int newMinX, int newMinY)
+        {
+            newMinX = Math.Clamp(Math.Min(newMinX, Width), Math.Min(-OffsetX, OffsetX), 0);
+            newMinY = Math.Clamp(Math.Min(newMinY, Height), Math.Min(-OffsetY, OffsetY), 0);
+
+            Offset = new Thickness(
+                Math.Clamp(OffsetX + newMinX, 0, MaxWidth),
+                Math.Clamp(OffsetY + newMinY, 0, MaxHeight),
+                0,
+                0);
+
+            int newWidth = Math.Clamp(Width - newMinX, 0, MaxWidth);
+            int newHeight = Math.Clamp(Height - newMinY, 0, MaxHeight);
+
+            int offsetX = Math.Abs(newWidth - Width);
+            int offsetY = Math.Abs(newHeight - Height);
+
+            ResizeCanvas(offsetX, offsetY, 0, 0, newWidth, newHeight);
+        }
+
+        private DoubleCords GetEdgePoints()
+        {
+            Coordinates smallestPixel = CoordinatesCalculator.FindMinEdgeNonTransparentPixel(LayerBitmap);
+            Coordinates biggestPixel = CoordinatesCalculator.FindMostEdgeNonTransparentPixel(LayerBitmap);
+
+            return new DoubleCords(smallestPixel, biggestPixel);
+        }
+
+        private void ResetOffset(BitmapPixelChanges pixels)
+        {
+            if (Width == 0 || Height == 0)
+            {
+                int offsetX = pixels.ChangedPixels.Min(x => x.Key.X);
+                int offsetY = pixels.ChangedPixels.Min(x => x.Key.Y);
+                Offset = new Thickness(offsetX, offsetY, 0, 0);
+            }
+        }
+
+        /// <summary>
+        ///     Resizes canvas to new size with specified offset.
+        /// </summary>
+        private void ResizeCanvas(int offsetX, int offsetY, int offsetXSrc, int offsetYSrc, int newWidth, int newHeight)
+        {
+            int iteratorHeight = Height > newHeight ? newHeight : Height;
+            int count = Width > newWidth ? newWidth : Width;
+
+            using (BitmapContext srcContext = LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
+            {
+                WriteableBitmap result = BitmapFactory.New(newWidth, newHeight);
+                using (BitmapContext destContext = result.GetBitmapContext())
+                {
+                    for (int line = 0; line < iteratorHeight; line++)
+                    {
+                        int srcOff = (((offsetYSrc + line) * Width) + offsetXSrc) * SizeOfArgb;
+                        int dstOff = (((offsetY + line) * newWidth) + offsetX) * SizeOfArgb;
+                        BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, count * SizeOfArgb);
+                    }
+
+                    LayerBitmap = result;
+                    Width = newWidth;
+                    Height = newHeight;
+                }
+            }
+        }
+    }
 }
 }

+ 71 - 71
PixiEditor/Models/Layers/SerializableLayer.cs

@@ -1,77 +1,77 @@
-using System;
-using System.Linq;
-
-namespace PixiEditor.Models.Layers
-{
-    [Serializable]
-    public class SerializableLayer
-    {
-        public SerializableLayer(Layer layer)
-        {
-            Name = layer.Name;
-            Width = layer.Width;
-            Height = layer.Height;
-            BitmapBytes = layer.ConvertBitmapToBytes();
-            IsVisible = layer.IsVisible;
-            OffsetX = (int)layer.Offset.Left;
-            OffsetY = (int)layer.Offset.Top;
-            Opacity = layer.Opacity;
-            MaxWidth = layer.MaxWidth;
-            MaxHeight = layer.MaxHeight;
-        }
-
-        public string Name { get; set; }
-
-        public int Width { get; set; }
-
-        public int Height { get; set; }
-
-        public int MaxWidth { get; set; }
-
-        public int MaxHeight { get; set; }
-
-        public byte[] BitmapBytes { get; set; }
-
-        public bool IsVisible { get; set; }
-
-        public int OffsetX { get; set; }
-
-        public int OffsetY { get; set; }
-
-        public float Opacity { get; set; }
-
-        public override bool Equals(object? obj)
-        {
+using System;
+using System.Linq;
+
+namespace PixiEditor.Models.Layers
+{
+    [Serializable]
+    public class SerializableLayer
+    {
+        public SerializableLayer(Layer layer)
+        {
+            Name = layer.Name;
+            Width = layer.Width;
+            Height = layer.Height;
+            BitmapBytes = layer.ConvertBitmapToBytes();
+            IsVisible = layer.IsVisible;
+            OffsetX = (int)layer.Offset.Left;
+            OffsetY = (int)layer.Offset.Top;
+            Opacity = layer.Opacity;
+            MaxWidth = layer.MaxWidth;
+            MaxHeight = layer.MaxHeight;
+        }
+
+        public string Name { get; set; }
+
+        public int Width { get; set; }
+
+        public int Height { get; set; }
+
+        public int MaxWidth { get; set; }
+
+        public int MaxHeight { get; set; }
+
+        public byte[] BitmapBytes { get; set; }
+
+        public bool IsVisible { get; set; }
+
+        public int OffsetX { get; set; }
+
+        public int OffsetY { get; set; }
+
+        public float Opacity { get; set; }
+
+        public override bool Equals(object obj)
+        {
             if (obj == null || obj.GetType() != typeof(SerializableLayer))
             if (obj == null || obj.GetType() != typeof(SerializableLayer))
             {
             {
                 return false;
                 return false;
             }
             }
 
 
-            SerializableLayer layer = (SerializableLayer)obj;
-
-            return Equals(layer);
-        }
-
-        protected bool Equals(SerializableLayer other)
-        {
-            return Name == other.Name && Width == other.Width && Height == other.Height && MaxWidth == other.MaxWidth && MaxHeight == other.MaxHeight &&
-                   BitmapBytes.SequenceEqual(other.BitmapBytes) && IsVisible == other.IsVisible && OffsetX == other.OffsetX && OffsetY == other.OffsetY && Opacity.Equals(other.Opacity);
-        }
-
-        public override int GetHashCode()
-        {
-            HashCode hashCode = new HashCode();
-            hashCode.Add(Name);
-            hashCode.Add(Width);
-            hashCode.Add(Height);
-            hashCode.Add(MaxWidth);
-            hashCode.Add(MaxHeight);
-            hashCode.Add(BitmapBytes);
-            hashCode.Add(IsVisible);
-            hashCode.Add(OffsetX);
-            hashCode.Add(OffsetY);
-            hashCode.Add(Opacity);
-            return hashCode.ToHashCode();
-        }
-    }
+            SerializableLayer layer = (SerializableLayer)obj;
+
+            return Equals(layer);
+        }
+
+        public override int GetHashCode()
+        {
+            HashCode hashCode = default(HashCode);
+            hashCode.Add(Name);
+            hashCode.Add(Width);
+            hashCode.Add(Height);
+            hashCode.Add(MaxWidth);
+            hashCode.Add(MaxHeight);
+            hashCode.Add(BitmapBytes);
+            hashCode.Add(IsVisible);
+            hashCode.Add(OffsetX);
+            hashCode.Add(OffsetY);
+            hashCode.Add(Opacity);
+            return hashCode.ToHashCode();
+        }
+
+        protected bool Equals(SerializableLayer other)
+        {
+            return Name == other.Name && Width == other.Width && Height == other.Height && MaxWidth == other.MaxWidth && MaxHeight == other.MaxHeight &&
+                   BitmapBytes.SequenceEqual(other.BitmapBytes) && IsVisible == other.IsVisible && OffsetX == other.OffsetX && OffsetY == other.OffsetY && Opacity.Equals(other.Opacity);
+        }
+    }
 }
 }

+ 8 - 8
PixiEditor/Models/Position/Coordinates.cs

@@ -4,20 +4,15 @@ namespace PixiEditor.Models.Position
 {
 {
     public struct Coordinates
     public struct Coordinates
     {
     {
-        public int X { get; set; }
-
-        public int Y { get; set; }
-
         public Coordinates(int x, int y)
         public Coordinates(int x, int y)
         {
         {
             X = x;
             X = x;
             Y = y;
             Y = y;
         }
         }
 
 
-        public override string ToString()
-        {
-            return $"{X}, {Y}";
-        }
+        public int X { get; set; }
+
+        public int Y { get; set; }
 
 
         public static bool operator ==(Coordinates c1, Coordinates c2)
         public static bool operator ==(Coordinates c1, Coordinates c2)
         {
         {
@@ -29,6 +24,11 @@ namespace PixiEditor.Models.Position
             return !(c1 == c2);
             return !(c1 == c2);
         }
         }
 
 
+        public override string ToString()
+        {
+            return $"{X}, {Y}";
+        }
+
         public override bool Equals(object obj)
         public override bool Equals(object obj)
         {
         {
             if (obj is Coordinates coords)
             if (obj is Coordinates coords)

+ 15 - 22
PixiEditor/Models/Position/CoordinatesCalculator.cs

@@ -8,25 +8,24 @@ namespace PixiEditor.Models.Position
     public static class CoordinatesCalculator
     public static class CoordinatesCalculator
     {
     {
         /// <summary>
         /// <summary>
-        ///     Calculates center of thickness * thickness rectangle
+        ///     Calculates center of thickness * thickness rectangle.
         /// </summary>
         /// </summary>
-        /// <param name="startPosition">Top left position of rectangle</param>
-        /// <param name="thickness">Thickness of rectangle</param>
-        /// <returns></returns>
+        /// <param name="startPosition">Top left position of rectangle.</param>
+        /// <param name="thickness">Thickness of rectangle.</param>
         public static DoubleCords CalculateThicknessCenter(Coordinates startPosition, int thickness)
         public static DoubleCords CalculateThicknessCenter(Coordinates startPosition, int thickness)
         {
         {
             int x1, x2, y1, y2;
             int x1, x2, y1, y2;
             if (thickness % 2 == 0)
             if (thickness % 2 == 0)
             {
             {
-                x2 = startPosition.X + thickness / 2;
-                y2 = startPosition.Y + thickness / 2;
+                x2 = startPosition.X + (thickness / 2);
+                y2 = startPosition.Y + (thickness / 2);
                 x1 = x2 - thickness;
                 x1 = x2 - thickness;
                 y1 = y2 - thickness;
                 y1 = y2 - thickness;
             }
             }
             else
             else
             {
             {
-                x2 = startPosition.X + (thickness - 1) / 2 + 1;
-                y2 = startPosition.Y + (thickness - 1) / 2 + 1;
+                x2 = startPosition.X + ((thickness - 1) / 2) + 1;
+                y2 = startPosition.Y + ((thickness - 1) / 2) + 1;
                 x1 = x2 - thickness;
                 x1 = x2 - thickness;
                 y1 = y2 - thickness;
                 y1 = y2 - thickness;
             }
             }
@@ -42,13 +41,12 @@ namespace PixiEditor.Models.Position
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Calculates coordinates of rectangle by edge points x1, y1, x2, y2
+        ///     Calculates coordinates of rectangle by edge points x1, y1, x2, y2.
         /// </summary>
         /// </summary>
-        /// <param name="x1">Top left x point</param>
-        /// <param name="y1">Top left y position</param>
-        /// <param name="x2">Bottom right x position</param>
-        /// <param name="y2">Bottom right Y position</param>
-        /// <returns></returns>
+        /// <param name="x1">Top left x point.</param>
+        /// <param name="y1">Top left y position.</param>
+        /// <param name="x2">Bottom right x position.</param>
+        /// <param name="y2">Bottom right Y position.</param>
         public static Coordinates[] RectangleToCoordinates(int x1, int y1, int x2, int y2)
         public static Coordinates[] RectangleToCoordinates(int x1, int y1, int x2, int y2)
         {
         {
             x2++;
             x2++;
@@ -67,25 +65,20 @@ namespace PixiEditor.Models.Position
 
 
         public static Coordinates[] RectangleToCoordinates(DoubleCords coordinates)
         public static Coordinates[] RectangleToCoordinates(DoubleCords coordinates)
         {
         {
-            return RectangleToCoordinates(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X,
-                coordinates.Coords2.Y);
+            return RectangleToCoordinates(coordinates.Coords1.X, coordinates.Coords1.Y, coordinates.Coords2.X, coordinates.Coords2.Y);
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Returns first pixel coordinates in bitmap that is most top left on canvas
+        ///     Returns first pixel coordinates in bitmap that is most top left on canvas.
         /// </summary>
         /// </summary>
-        /// <param name="bitmap"></param>
-        /// <returns></returns>
         public static Coordinates FindMinEdgeNonTransparentPixel(WriteableBitmap bitmap)
         public static Coordinates FindMinEdgeNonTransparentPixel(WriteableBitmap bitmap)
         {
         {
             return new Coordinates(FindMinXNonTransparent(bitmap), FindMinYNonTransparent(bitmap));
             return new Coordinates(FindMinXNonTransparent(bitmap), FindMinYNonTransparent(bitmap));
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Returns last pixel coordinates that is most bottom right
+        ///     Returns last pixel coordinates that is most bottom right.
         /// </summary>
         /// </summary>
-        /// <param name="bitmap"></param>
-        /// <returns></returns>
         public static Coordinates FindMostEdgeNonTransparentPixel(WriteableBitmap bitmap)
         public static Coordinates FindMostEdgeNonTransparentPixel(WriteableBitmap bitmap)
         {
         {
             return new Coordinates(FindMaxXNonTransparent(bitmap), FindMaxYNonTransparent(bitmap));
             return new Coordinates(FindMaxXNonTransparent(bitmap), FindMaxYNonTransparent(bitmap));

+ 13 - 13
PixiEditor/Models/Position/DoubleCords.cs

@@ -1,15 +1,15 @@
-namespace PixiEditor.Models.Position
-{
-    public struct DoubleCords
-    {
-        public Coordinates Coords1 { get; set; }
+namespace PixiEditor.Models.Position
+{
+    public struct DoubleCords
+    {
+        public DoubleCords(Coordinates cords1, Coordinates cords2)
+        {
+            Coords1 = cords1;
+            Coords2 = cords2;
+        }
 
 
-        public Coordinates Coords2 { get; set; }
-
-        public DoubleCords(Coordinates cords1, Coordinates cords2)
-        {
-            Coords1 = cords1;
-            Coords2 = cords2;
-        }
-    }
+        public Coordinates Coords1 { get; set; }
+
+        public Coordinates Coords2 { get; set; }
+    }
 }
 }

+ 5 - 5
PixiEditor/Models/Position/MousePositionConverter.cs

@@ -5,15 +5,15 @@ namespace PixiEditor.Models.Position
 {
 {
     public static class MousePositionConverter
     public static class MousePositionConverter
     {
     {
-        public static Coordinates CurrentCoordinates { get; set; }
-
-        [DllImport("user32.dll")]
-        private static extern bool GetCursorPos(out Point point);
-
+        public static Coordinates CurrentCoordinates { get; set; }
+
         public static Point GetCursorPosition()
         public static Point GetCursorPosition()
         {
         {
             GetCursorPos(out Point point);
             GetCursorPos(out Point point);
             return point;
             return point;
         }
         }
+
+        [DllImport("user32.dll")]
+        private static extern bool GetCursorPos(out Point point);
     }
     }
 }
 }

+ 10 - 5
PixiEditor/Models/Tools/ShapeTool.cs

@@ -35,32 +35,37 @@ namespace PixiEditor.Models.Tools
             return output.Distinct();
             return output.Distinct();
         }
         }
 
 
-        protected DoubleCords CalculateCoordinatesForShapeRotation(Coordinates startingCords,
+        protected DoubleCords CalculateCoordinatesForShapeRotation(
+            Coordinates startingCords,
             Coordinates secondCoordinates)
             Coordinates secondCoordinates)
         {
         {
             Coordinates currentCoordinates = secondCoordinates;
             Coordinates currentCoordinates = secondCoordinates;
 
 
             if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
             if (startingCords.X > currentCoordinates.X && startingCords.Y > currentCoordinates.Y)
             {
             {
-                return new DoubleCords(new Coordinates(currentCoordinates.X, currentCoordinates.Y),
+                return new DoubleCords(
+                    new Coordinates(currentCoordinates.X, currentCoordinates.Y),
                     new Coordinates(startingCords.X, startingCords.Y));
                     new Coordinates(startingCords.X, startingCords.Y));
             }
             }
 
 
             if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
             if (startingCords.X < currentCoordinates.X && startingCords.Y < currentCoordinates.Y)
             {
             {
-                return new DoubleCords(new Coordinates(startingCords.X, startingCords.Y),
+                return new DoubleCords(
+                    new Coordinates(startingCords.X, startingCords.Y),
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y));
                     new Coordinates(currentCoordinates.X, currentCoordinates.Y));
             }
             }
 
 
             if (startingCords.Y > currentCoordinates.Y)
             if (startingCords.Y > currentCoordinates.Y)
             {
             {
-                return new DoubleCords(new Coordinates(startingCords.X, currentCoordinates.Y),
+                return new DoubleCords(
+                    new Coordinates(startingCords.X, currentCoordinates.Y),
                     new Coordinates(currentCoordinates.X, startingCords.Y));
                     new Coordinates(currentCoordinates.X, startingCords.Y));
             }
             }
 
 
             if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
             if (startingCords.X > currentCoordinates.X && startingCords.Y <= currentCoordinates.Y)
             {
             {
-                return new DoubleCords(new Coordinates(currentCoordinates.X, startingCords.Y),
+                return new DoubleCords(
+                    new Coordinates(currentCoordinates.X, startingCords.Y),
                     new Coordinates(startingCords.X, currentCoordinates.Y));
                     new Coordinates(startingCords.X, currentCoordinates.Y));
             }
             }
 
 

+ 42 - 38
PixiEditor/Models/Tools/ToolSettings/Settings/FloatSetting.cs

@@ -1,40 +1,44 @@
-using System.Windows.Data;
-using PixiEditor.Views;
-
-namespace PixiEditor.Models.Tools.ToolSettings.Settings
-{
-    public class FloatSetting : Setting<float>
-    {
-        public FloatSetting(string name, float initialValue, string label = "",
-            float min = float.NegativeInfinity, float max = float.PositiveInfinity)
-            : base(name)
-        {
-            Label = label;
-            Value = initialValue;
-            Min = min;
-            Max = max;
-            SettingControl = GenerateNumberInput();
-        }
-
-        public float Min { get; set; }
+using System.Windows.Data;
+using PixiEditor.Views;
 
 
-        public float Max { get; set; }
-
-        private NumberInput GenerateNumberInput()
-        {
-            NumberInput numbrInput = new NumberInput
-            {
-                Width = 40,
-                Height = 20,
-                Min = Min,
-                Max = Max
-            };
-            Binding binding = new Binding("Value")
-            {
-                Mode = BindingMode.TwoWay
-            };
-            numbrInput.SetBinding(NumberInput.ValueProperty, binding);
-            return numbrInput;
-        }
-    }
+namespace PixiEditor.Models.Tools.ToolSettings.Settings
+{
+    public class FloatSetting : Setting<float>
+    {
+        public FloatSetting(
+            string name,
+            float initialValue,
+            string label = "",
+            float min = float.NegativeInfinity,
+            float max = float.PositiveInfinity)
+            : base(name)
+        {
+            Label = label;
+            Value = initialValue;
+            Min = min;
+            Max = max;
+            SettingControl = GenerateNumberInput();
+        }
+
+        public float Min { get; set; }
+
+        public float Max { get; set; }
+
+        private NumberInput GenerateNumberInput()
+        {
+            NumberInput numbrInput = new NumberInput
+            {
+                Width = 40,
+                Height = 20,
+                Min = Min,
+                Max = Max
+            };
+            Binding binding = new Binding("Value")
+            {
+                Mode = BindingMode.TwoWay
+            };
+            numbrInput.SetBinding(NumberInput.ValueProperty, binding);
+            return numbrInput;
+        }
+    }
 }
 }

+ 0 - 20
PixiEditor/Models/Tools/ToolSettings/Settings/Setting.cs

@@ -3,26 +3,6 @@ using PixiEditor.Helpers;
 
 
 namespace PixiEditor.Models.Tools.ToolSettings.Settings
 namespace PixiEditor.Models.Tools.ToolSettings.Settings
 {
 {
-    public abstract class Setting<T> : Setting
-    {
-        private T value;
-
-        protected Setting(string name)
-            : base(name)
-        {
-        }
-
-        public T Value
-        {
-            get => value;
-            set
-            {
-                this.value = value;
-                RaisePropertyChanged("Value");
-            }
-        }
-    }
-
     public abstract class Setting : NotifyableObject
     public abstract class Setting : NotifyableObject
     {
     {
         protected Setting(string name)
         protected Setting(string name)

+ 22 - 0
PixiEditor/Models/Tools/ToolSettings/Settings/Setting{T}.cs

@@ -0,0 +1,22 @@
+namespace PixiEditor.Models.Tools.ToolSettings.Settings
+{
+    public abstract class Setting<T> : Setting
+    {
+        private T value;
+
+        protected Setting(string name)
+            : base(name)
+        {
+        }
+
+        public T Value
+        {
+            get => value;
+            set
+            {
+                this.value = value;
+                RaisePropertyChanged("Value");
+            }
+        }
+    }
+}

+ 1 - 1
PixiEditor/Models/Tools/ToolSettings/Toolbars/BasicToolbar.cs

@@ -3,7 +3,7 @@
 namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
 namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
 {
 {
     /// <summary>
     /// <summary>
-    ///     Toolbar with size setting
+    ///     Toolbar with size setting.
     /// </summary>
     /// </summary>
     public class BasicToolbar : Toolbar
     public class BasicToolbar : Toolbar
     {
     {

+ 2 - 2
PixiEditor/Models/Tools/ToolSettings/Toolbars/Toolbar.cs

@@ -15,7 +15,7 @@ namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
         /// <summary>
         /// <summary>
         ///     Gets setting in toolbar by name.
         ///     Gets setting in toolbar by name.
         /// </summary>
         /// </summary>
-        /// <param name="name">Setting name, non case sensitive</param>
+        /// <param name="name">Setting name, non case sensitive.</param>
         /// <returns></returns>
         /// <returns></returns>
         public virtual Setting GetSetting(string name)
         public virtual Setting GetSetting(string name)
         {
         {
@@ -25,7 +25,7 @@ namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
         /// <summary>
         /// <summary>
         ///     Gets setting of given type T in toolbar by name.
         ///     Gets setting of given type T in toolbar by name.
         /// </summary>
         /// </summary>
-        /// <param name="name">Setting name, non case sensitive</param>
+        /// <param name="name">Setting name, non case sensitive.</param>
         /// <returns></returns>
         /// <returns></returns>
         public T GetSetting<T>(string name)
         public T GetSetting<T>(string name)
             where T : Setting
             where T : Setting

+ 79 - 74
PixiEditor/Models/Tools/Tools/BrightnessTool.cs

@@ -1,90 +1,95 @@
-using System;
-using System.Collections.Generic;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Media;
-using PixiEditor.Models.Colors;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Enums;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
-using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class BrightnessTool : BitmapOperationTool
-    {
-        private const float CorrectionFactor = 5f; // Initial correction factor
-
-        private readonly List<Coordinates> pixelsVisited = new List<Coordinates>();
-
-        public BrightnessTool()
-        {
-            Tooltip = "Makes pixel brighter or darker pixel (U). Hold Ctrl to make pixel darker.";
-            Toolbar = new BrightnessToolToolbar(CorrectionFactor);
-        }
-
-        public override ToolType ToolType => ToolType.Brightness;
+using System;
+using System.Collections.Generic;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using PixiEditor.Models.Colors;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Enums;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
+using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 
 
-        public BrightnessMode Mode { get; set; } = BrightnessMode.Default;
-
-        public override void OnMouseDown(MouseEventArgs e)
-        {
-            pixelsVisited.Clear();
-        }
-
-        public override LayerChange[] Use(Layer layer, Coordinates[] coordinates, Color color)
-        {
-            int toolSize = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
-            float correctionFactor = Toolbar.GetSetting<FloatSetting>("CorrectionFactor").Value;
-            Enum.TryParse((Toolbar.GetSetting<DropdownSetting>("Mode")?.Value as ComboBoxItem)?.Content as string, out BrightnessMode mode);
-            Mode = mode;
-
-            LayerChange[] layersChanges = new LayerChange[1];
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class BrightnessTool : BitmapOperationTool
+    {
+        private const float CorrectionFactor = 5f; // Initial correction factor
+
+        private readonly List<Coordinates> pixelsVisited = new List<Coordinates>();
+
+        public BrightnessTool()
+        {
+            Tooltip = "Makes pixel brighter or darker pixel (U). Hold Ctrl to make pixel darker.";
+            Toolbar = new BrightnessToolToolbar(CorrectionFactor);
+        }
+
+        public override ToolType ToolType => ToolType.Brightness;
+
+        public BrightnessMode Mode { get; set; } = BrightnessMode.Default;
+
+        public override void OnMouseDown(MouseEventArgs e)
+        {
+            pixelsVisited.Clear();
+        }
+
+        public override LayerChange[] Use(Layer layer, Coordinates[] coordinates, Color color)
+        {
+            int toolSize = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
+            float correctionFactor = Toolbar.GetSetting<FloatSetting>("CorrectionFactor").Value;
+            Enum.TryParse((Toolbar.GetSetting<DropdownSetting>("Mode")?.Value as ComboBoxItem)?.Content as string, out BrightnessMode mode);
+            Mode = mode;
+
+            LayerChange[] layersChanges = new LayerChange[1];
             if (Keyboard.IsKeyDown(Key.LeftCtrl))
             if (Keyboard.IsKeyDown(Key.LeftCtrl))
             {
             {
-                layersChanges[0] = new LayerChange(ChangeBrightness(layer, coordinates[0], toolSize, -correctionFactor),
+                layersChanges[0] = new LayerChange(
+                    ChangeBrightness(layer, coordinates[0], toolSize, -correctionFactor),
                     layer);
                     layer);
             }
             }
             else
             else
             {
             {
-                layersChanges[0] = new LayerChange(ChangeBrightness(layer, coordinates[0], toolSize, correctionFactor),
+                layersChanges[0] = new LayerChange(
+                    ChangeBrightness(layer, coordinates[0], toolSize, correctionFactor),
                     layer);
                     layer);
             }
             }
 
 
-            return layersChanges;
-        }
-
-        public BitmapPixelChanges ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize,
-            float correctionFactor)
-        {
-            DoubleCords centeredCoords = CoordinatesCalculator.CalculateThicknessCenter(coordinates, toolSize);
-            Coordinates[] rectangleCoordinates = CoordinatesCalculator.RectangleToCoordinates(centeredCoords.Coords1.X,
-                centeredCoords.Coords1.Y,
-                centeredCoords.Coords2.X, centeredCoords.Coords2.Y);
-            BitmapPixelChanges changes = new BitmapPixelChanges(new Dictionary<Coordinates, Color>());
-
-            for (int i = 0; i < rectangleCoordinates.Length; i++)
-            {
-                if (Mode == BrightnessMode.Default)
-                {
+            return layersChanges;
+        }
+
+        public BitmapPixelChanges ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
+        {
+            DoubleCords centeredCoords = CoordinatesCalculator.CalculateThicknessCenter(coordinates, toolSize);
+            Coordinates[] rectangleCoordinates = CoordinatesCalculator.RectangleToCoordinates(
+                centeredCoords.Coords1.X,
+                centeredCoords.Coords1.Y,
+                centeredCoords.Coords2.X,
+                centeredCoords.Coords2.Y);
+            BitmapPixelChanges changes = new BitmapPixelChanges(new Dictionary<Coordinates, Color>());
+
+            for (int i = 0; i < rectangleCoordinates.Length; i++)
+            {
+                if (Mode == BrightnessMode.Default)
+                {
                     if (pixelsVisited.Contains(rectangleCoordinates[i]))
                     if (pixelsVisited.Contains(rectangleCoordinates[i]))
                     {
                     {
                         continue;
                         continue;
                     }
                     }
 
 
-                    pixelsVisited.Add(rectangleCoordinates[i]);
-                }
-
-                Color pixel = layer.GetPixelWithOffset(rectangleCoordinates[i].X, rectangleCoordinates[i].Y);
-                Color newColor = ExColor.ChangeColorBrightness(Color.FromArgb(pixel.A, pixel.R, pixel.G, pixel.B),
-                    correctionFactor);
-                changes.ChangedPixels.Add(new Coordinates(rectangleCoordinates[i].X, rectangleCoordinates[i].Y),
-                    newColor);
-            }
-
-            return changes;
-        }
-    }
+                    pixelsVisited.Add(rectangleCoordinates[i]);
+                }
+
+                Color pixel = layer.GetPixelWithOffset(rectangleCoordinates[i].X, rectangleCoordinates[i].Y);
+                Color newColor = ExColor.ChangeColorBrightness(
+                    Color.FromArgb(pixel.A, pixel.R, pixel.G, pixel.B),
+                    correctionFactor);
+                changes.ChangedPixels.Add(
+                    new Coordinates(rectangleCoordinates[i].X, rectangleCoordinates[i].Y),
+                    newColor);
+            }
+
+            return changes;
+        }
+    }
 }
 }

+ 18 - 18
PixiEditor/Models/Tools/Tools/CircleTool.cs

@@ -39,13 +39,12 @@ namespace PixiEditor.Models.Tools.Tools
         /// <summary>
         /// <summary>
         ///     Calculates ellipse points for specified coordinates and thickness.
         ///     Calculates ellipse points for specified coordinates and thickness.
         /// </summary>
         /// </summary>
-        /// <param name="startCoordinates">Top left coordinate of ellipse</param>
-        /// <param name="endCoordinates">Bottom right coordinate of ellipse</param>
-        /// <param name="thickness">Thickness of ellipse</param>
-        /// <param name="filled">Should ellipse be filled</param>
-        /// <returns>Coordinates for ellipse</returns>
-        public IEnumerable<Coordinates> CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, int thickness,
-            bool filled)
+        /// <param name="startCoordinates">Top left coordinate of ellipse.</param>
+        /// <param name="endCoordinates">Bottom right coordinate of ellipse.</param>
+        /// <param name="thickness">Thickness of ellipse.</param>
+        /// <param name="filled">Should ellipse be filled.</param>
+        /// <returns>Coordinates for ellipse.</returns>
+        public IEnumerable<Coordinates> CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, int thickness, bool filled)
         {
         {
             List<Coordinates> output = new List<Coordinates>();
             List<Coordinates> output = new List<Coordinates>();
             IEnumerable<Coordinates> outline = CreateEllipse(startCoordinates, endCoordinates, thickness);
             IEnumerable<Coordinates> outline = CreateEllipse(startCoordinates, endCoordinates, thickness);
@@ -62,10 +61,10 @@ namespace PixiEditor.Models.Tools.Tools
         /// <summary>
         /// <summary>
         ///     Calculates ellipse points for specified coordinates and thickness.
         ///     Calculates ellipse points for specified coordinates and thickness.
         /// </summary>
         /// </summary>
-        /// <param name="startCoordinates">Top left coordinate of ellipse</param>
-        /// <param name="endCoordinates">Bottom right coordinate of ellipse</param>
-        /// <param name="thickness">Thickness of ellipse</param>
-        /// <returns>Coordinates for ellipse</returns>
+        /// <param name="startCoordinates">Top left coordinate of ellipse.</param>
+        /// <param name="endCoordinates">Bottom right coordinate of ellipse.</param>
+        /// <param name="thickness">Thickness of ellipse.</param>
+        /// <returns>Coordinates for ellipse.</returns>
         public IEnumerable<Coordinates> CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, int thickness)
         public IEnumerable<Coordinates> CreateEllipse(Coordinates startCoordinates, Coordinates endCoordinates, int thickness)
         {
         {
             double radiusX = (endCoordinates.X - startCoordinates.X) / 2.0;
             double radiusX = (endCoordinates.X - startCoordinates.X) / 2.0;
@@ -112,9 +111,9 @@ namespace PixiEditor.Models.Tools.Tools
                 // calculate next pixel coords
                 // calculate next pixel coords
                 currentX++;
                 currentX++;
 
 
-                if (Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX, 2) +
-                    Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY - 0.5, 2) -
-                    Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2) >= 0)
+                if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX, 2)) +
+                    (Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY - 0.5, 2)) -
+                    (Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2)) >= 0)
                 {
                 {
                     currentY--;
                     currentY--;
                 }
                 }
@@ -123,7 +122,8 @@ namespace PixiEditor.Models.Tools.Tools
                 double derivativeX = 2 * Math.Pow(halfHeight, 2) * (currentX - centerX);
                 double derivativeX = 2 * Math.Pow(halfHeight, 2) * (currentX - centerX);
                 double derivativeY = 2 * Math.Pow(halfWidth, 2) * (currentY - centerY);
                 double derivativeY = 2 * Math.Pow(halfWidth, 2) * (currentY - centerY);
                 currentSlope = -(derivativeX / derivativeY);
                 currentSlope = -(derivativeX / derivativeY);
-            } while (currentSlope > -1 && currentY - centerY > 0.5);
+            }
+            while (currentSlope > -1 && currentY - centerY > 0.5);
 
 
             // from middle to 0
             // from middle to 0
             while (currentY - centerY >= 0)
             while (currentY - centerY >= 0)
@@ -131,9 +131,9 @@ namespace PixiEditor.Models.Tools.Tools
                 outputCoordinates.AddRange(GetRegionPoints(currentX, centerX, currentY, centerY));
                 outputCoordinates.AddRange(GetRegionPoints(currentX, centerX, currentY, centerY));
 
 
                 currentY--;
                 currentY--;
-                if (Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX + 0.5, 2) +
-                    Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY, 2) -
-                    Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2) < 0)
+                if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX + 0.5, 2)) +
+                    (Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY, 2)) -
+                    (Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2)) < 0)
                 {
                 {
                     currentX++;
                     currentX++;
                 }
                 }

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

@@ -27,8 +27,7 @@ namespace PixiEditor.Models.Tools.Tools
             {
             {
                 using (Graphics graphics = Graphics.FromImage(bitmap))
                 using (Graphics graphics = Graphics.FromImage(bitmap))
                 {
                 {
-                    graphics.CopyFromScreen(MousePositionConverter.GetCursorPosition(), new Point(0, 0),
-                        new Size(1, 1));
+                    graphics.CopyFromScreen(MousePositionConverter.GetCursorPosition(), new Point(0, 0), new Size(1, 1));
                 }
                 }
 
 
                 color = bitmap.GetPixel(0, 0);
                 color = bitmap.GetPixel(0, 0);

+ 11 - 10
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -24,8 +24,11 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             BitmapPixelChanges pixels =
             BitmapPixelChanges pixels =
                 BitmapPixelChanges.FromSingleColoredArray(
                 BitmapPixelChanges.FromSingleColoredArray(
-                    CreateLine(coordinates,
-                        Toolbar.GetSetting<SizeSetting>("ToolSize").Value, CapType.Square, CapType.Square), color);
+                    CreateLine(
+                        coordinates,
+                        Toolbar.GetSetting<SizeSetting>("ToolSize").Value,
+                        CapType.Square,
+                        CapType.Square), color);
             return Only(pixels, layer);
             return Only(pixels, layer);
         }
         }
 
 
@@ -34,8 +37,7 @@ namespace PixiEditor.Models.Tools.Tools
             return CreateLine(new[] { end, start }, thickness, CapType.Square, CapType.Square);
             return CreateLine(new[] { end, start }, thickness, CapType.Square, CapType.Square);
         }
         }
 
 
-        public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness, CapType startCap,
-            CapType endCap)
+        public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
         {
         {
             return CreateLine(new[] { end, start }, thickness, startCap, endCap);
             return CreateLine(new[] { end, start }, thickness, startCap, endCap);
         }
         }
@@ -46,8 +48,7 @@ namespace PixiEditor.Models.Tools.Tools
             Coordinates latestCoordinates = coordinates[0];
             Coordinates latestCoordinates = coordinates[0];
             if (thickness == 1)
             if (thickness == 1)
             {
             {
-                return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X,
-                    latestCoordinates.Y);
+                return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X, latestCoordinates.Y);
             }
             }
 
 
             return GetLinePoints(startingCoordinates, latestCoordinates, thickness, startCap, endCap);
             return GetLinePoints(startingCoordinates, latestCoordinates, thickness, startCap, endCap);
@@ -82,17 +83,17 @@ namespace PixiEditor.Models.Tools.Tools
                     {
                     {
                         return GetRoundCap(position, thickness); // Round cap is not working very well, circle tool must be improved
                         return GetRoundCap(position, thickness); // Round cap is not working very well, circle tool must be improved
                     }
                     }
+
                 default:
                 default:
                     return GetThickShape(new[] { position }, thickness);
                     return GetThickShape(new[] { position }, thickness);
             }
             }
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Gets points for rounded cap on specified position and thickness
+        ///     Gets points for rounded cap on specified position and thickness.
         /// </summary>
         /// </summary>
-        /// <param name="position">Starting position of cap</param>
-        /// <param name="thickness">Thickness of cap</param>
-        /// <returns></returns>
+        /// <param name="position">Starting position of cap.</param>
+        /// <param name="thickness">Thickness of cap.</param>
         private IEnumerable<Coordinates> GetRoundCap(Coordinates position, int thickness)
         private IEnumerable<Coordinates> GetRoundCap(Coordinates position, int thickness)
         {
         {
             CircleTool circle = new CircleTool();
             CircleTool circle = new CircleTool();

+ 17 - 16
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -135,6 +135,22 @@ namespace PixiEditor.Models.Tools.Tools
             return result;
             return result;
         }
         }
 
 
+        public BitmapPixelChanges MoveSelection(Layer layer, Coordinates[] mouseMove)
+        {
+            Coordinates end = mouseMove[0];
+
+            currentSelection = TranslateSelection(end, out Coordinates[] previousSelection);
+            if (updateViewModelSelection)
+            {
+                ViewModelMain.Current.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
+            }
+
+            ClearSelectedPixels(layer, previousSelection);
+
+            lastMouseMove = end;
+            return BitmapPixelChanges.FromArrays(currentSelection, startPixelColors[layer]);
+        }
+
         private void ApplyOffsets(object[] parameters)
         private void ApplyOffsets(object[] parameters)
         {
         {
             Dictionary<Layer, Thickness> offsets = (Dictionary<Layer, Thickness>)parameters[0];
             Dictionary<Layer, Thickness> offsets = (Dictionary<Layer, Thickness>)parameters[0];
@@ -165,22 +181,6 @@ namespace PixiEditor.Models.Tools.Tools
             return pixels;
             return pixels;
         }
         }
 
 
-        public BitmapPixelChanges MoveSelection(Layer layer, Coordinates[] mouseMove)
-        {
-            Coordinates end = mouseMove[0];
-
-            currentSelection = TranslateSelection(end, out Coordinates[] previousSelection);
-            if (updateViewModelSelection)
-            {
-                ViewModelMain.Current.ActiveSelection.SetSelection(currentSelection, SelectionType.New);
-            }
-
-            ClearSelectedPixels(layer, previousSelection);
-
-            lastMouseMove = end;
-            return BitmapPixelChanges.FromArrays(currentSelection, startPixelColors[layer]);
-        }
-
         private void ResetSelectionValues(Coordinates start)
         private void ResetSelectionValues(Coordinates start)
         {
         {
             lastStartMousePos = start;
             lastStartMousePos = start;
@@ -197,6 +197,7 @@ namespace PixiEditor.Models.Tools.Tools
             previousSelection = currentSelection.ToArray();
             previousSelection = currentSelection.ToArray();
             return Transform.Translate(previousSelection, translation);
             return Transform.Translate(previousSelection, translation);
         }
         }
+
         private void ClearSelectedPixels(Layer layer, Coordinates[] selection)
         private void ClearSelectedPixels(Layer layer, Coordinates[] selection)
         {
         {
             if (!clearedPixels.ContainsKey(layer) || clearedPixels[layer] == false)
             if (!clearedPixels.ContainsKey(layer) || clearedPixels[layer] == false)

+ 21 - 21
PixiEditor/Models/Tools/Tools/RectangleTool.cs

@@ -30,8 +30,8 @@ namespace PixiEditor.Models.Tools.Tools
             {
             {
                 Color fillColor = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 Color fillColor = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 pixels.ChangedPixels.AddRangeOverride(
                 pixels.ChangedPixels.AddRangeOverride(
-                    BitmapPixelChanges.FromSingleColoredArray
-                            (CalculateFillForRectangle(coordinates[^1], coordinates[0], thickness), fillColor)
+                    BitmapPixelChanges.FromSingleColoredArray(
+                            CalculateFillForRectangle(coordinates[^1], coordinates[0], thickness), fillColor)
                         .ChangedPixels);
                         .ChangedPixels);
             }
             }
 
 
@@ -67,25 +67,6 @@ namespace PixiEditor.Models.Tools.Tools
             return CreateRectangle(new[] { end, start }, thickness);
             return CreateRectangle(new[] { end, start }, thickness);
         }
         }
 
 
-        private IEnumerable<Coordinates> CalculateRectanglePoints(DoubleCords coordinates)
-        {
-            List<Coordinates> finalCoordinates = new List<Coordinates>();
-
-            for (int i = coordinates.Coords1.X; i < coordinates.Coords2.X + 1; i++)
-            {
-                finalCoordinates.Add(new Coordinates(i, coordinates.Coords1.Y));
-                finalCoordinates.Add(new Coordinates(i, coordinates.Coords2.Y));
-            }
-
-            for (int i = coordinates.Coords1.Y + 1; i <= coordinates.Coords2.Y - 1; i++)
-            {
-                finalCoordinates.Add(new Coordinates(coordinates.Coords1.X, i));
-                finalCoordinates.Add(new Coordinates(coordinates.Coords2.X, i));
-            }
-
-            return finalCoordinates;
-        }
-
         public IEnumerable<Coordinates> CalculateFillForRectangle(Coordinates start, Coordinates end, int thickness)
         public IEnumerable<Coordinates> CalculateFillForRectangle(Coordinates start, Coordinates end, int thickness)
         {
         {
             int offset = (int)Math.Ceiling(thickness / 2f);
             int offset = (int)Math.Ceiling(thickness / 2f);
@@ -118,5 +99,24 @@ namespace PixiEditor.Models.Tools.Tools
 
 
             return filledCoordinates.Distinct();
             return filledCoordinates.Distinct();
         }
         }
+
+        private IEnumerable<Coordinates> CalculateRectanglePoints(DoubleCords coordinates)
+        {
+            List<Coordinates> finalCoordinates = new List<Coordinates>();
+
+            for (int i = coordinates.Coords1.X; i < coordinates.Coords2.X + 1; i++)
+            {
+                finalCoordinates.Add(new Coordinates(i, coordinates.Coords1.Y));
+                finalCoordinates.Add(new Coordinates(i, coordinates.Coords2.Y));
+            }
+
+            for (int i = coordinates.Coords1.Y + 1; i <= coordinates.Coords2.Y - 1; i++)
+            {
+                finalCoordinates.Add(new Coordinates(coordinates.Coords1.X, i));
+                finalCoordinates.Add(new Coordinates(coordinates.Coords2.X, i));
+            }
+
+            return finalCoordinates;
+        }
     }
     }
 }
 }

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

@@ -1,91 +1,91 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows.Controls;
-using System.Windows.Input;
-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.ViewModels;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class SelectTool : ReadonlyTool
-    {
-        private Selection oldSelection;
-        public SelectionType SelectionType = SelectionType.Add;
-
-        public SelectTool()
-        {
-            Tooltip = "Selects area. (M)";
-            Toolbar = new SelectToolToolbar();
-        }
-
-        public override ToolType ToolType => ToolType.Select;
-
-        public override void OnMouseDown(MouseEventArgs e)
-        {
-            Enum.TryParse((Toolbar.GetSetting<DropdownSetting>("Mode")?.Value as ComboBoxItem)?.Content as string, out SelectionType);
-
-            oldSelection = null;
-            if (ViewModelMain.Current.ActiveSelection != null &&
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Controls;
+using System.Windows.Input;
+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.ViewModels;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class SelectTool : ReadonlyTool
+    {
+        private Selection oldSelection;
+
+        public SelectTool()
+        {
+            Tooltip = "Selects area. (M)";
+            Toolbar = new SelectToolToolbar();
+        }
+
+        public SelectionType SelectionType { get; set; } = SelectionType.Add;
+
+        public override ToolType ToolType => ToolType.Select;
+
+        public override void OnMouseDown(MouseEventArgs e)
+        {
+            Enum.TryParse((Toolbar.GetSetting<DropdownSetting>("Mode")?.Value as ComboBoxItem)?.Content as string, out SelectionType selectionType);
+            SelectionType = selectionType;
+
+            oldSelection = null;
+            if (ViewModelMain.Current.ActiveSelection != null &&
                 ViewModelMain.Current.ActiveSelection.SelectedPoints != null)
                 ViewModelMain.Current.ActiveSelection.SelectedPoints != null)
             {
             {
                 oldSelection = ViewModelMain.Current.ActiveSelection;
                 oldSelection = ViewModelMain.Current.ActiveSelection;
             }
             }
-        }
-
-        public override void OnMouseUp(MouseEventArgs e)
-        {
+        }
+
+        public override void OnMouseUp(MouseEventArgs e)
+        {
             if (ViewModelMain.Current.ActiveSelection.SelectedPoints.Count() <= 1)
             if (ViewModelMain.Current.ActiveSelection.SelectedPoints.Count() <= 1)
             {
             {
                 // If we have not selected multiple points, clear the selection
                 // If we have not selected multiple points, clear the selection
                 ViewModelMain.Current.ActiveSelection.Clear();
                 ViewModelMain.Current.ActiveSelection.Clear();
             }
             }
 
 
-            UndoManager.AddUndoChange(new Change("ActiveSelection", oldSelection,
-                ViewModelMain.Current.ActiveSelection, "Select pixels"));
-        }
-
-        public override void Use(Coordinates[] pixels)
-        {
-            Select(pixels);
-        }
-
-        private void Select(Coordinates[] pixels)
-        {
-            IEnumerable<Coordinates> selection = GetRectangleSelectionForPoints(pixels[^1], pixels[0]);
-            ViewModelMain.Current.ActiveSelection.SetSelection(selection, SelectionType);
-        }
-
-        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>
-        /// <param name="document"></param>
-        /// <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));
-        }
-    }
+            UndoManager.AddUndoChange(new Change("ActiveSelection", oldSelection, ViewModelMain.Current.ActiveSelection, "Select pixels"));
+        }
+
+        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.ActiveSelection.SetSelection(selection, SelectionType);
+        }
+    }
 }
 }

+ 60 - 60
PixiEditor/Models/Tools/Tools/ZoomTool.cs

@@ -1,51 +1,51 @@
-using System;
-using System.Windows;
-using System.Windows.Input;
-using PixiEditor.Models.Position;
-using PixiEditor.ViewModels;
-
-namespace PixiEditor.Models.Tools.Tools
-{
-    public class ZoomTool : ReadonlyTool
-    {
-        public const float ZoomSensitivityMultiplier = 30f;
-        private readonly double pixelsPerZoomMultiplier;
-        private double startingX;
-        private readonly double workAreaWidth = SystemParameters.WorkArea.Width;
-
-        public ZoomTool()
-        {
-            HideHighlight = true;
-            CanStartOutsideCanvas = true;
-            Tooltip = "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
-            pixelsPerZoomMultiplier = workAreaWidth / ZoomSensitivityMultiplier;
-        }
-
-        public override ToolType ToolType => ToolType.Zoom;
-
-        public override void OnMouseDown(MouseEventArgs e)
-        {
-            startingX = MousePositionConverter.GetCursorPosition().X;
-            ViewModelMain.Current.ZoomPercentage = 100; // This resest the value, so callback in MainDrawingPanel can fire again later
-        }
-
-        public override void OnMouseMove(MouseEventArgs e)
-        {
-            if (e.LeftButton == MouseButtonState.Pressed)
-            {
-                double xPos = MousePositionConverter.GetCursorPosition().X;
-
-                double rawPercentDifference = (xPos - startingX) / pixelsPerZoomMultiplier; // negative - zoom out, positive - zoom in, linear
-                double finalPercentDifference = Math.Pow(2, rawPercentDifference) * 100.0; // less than 100 - zoom out, greater than 100 - zoom in
-                Zoom(finalPercentDifference);
-            }
-        }
-
-        public override void OnMouseUp(MouseEventArgs e)
-        {
-            if (e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released &&
-                startingX == MousePositionConverter.GetCursorPosition().X)
-            {
+using System;
+using System.Windows;
+using System.Windows.Input;
+using PixiEditor.Models.Position;
+using PixiEditor.ViewModels;
+
+namespace PixiEditor.Models.Tools.Tools
+{
+    public class ZoomTool : ReadonlyTool
+    {
+        public const float ZoomSensitivityMultiplier = 30f;
+        private readonly double pixelsPerZoomMultiplier;
+        private readonly double workAreaWidth = SystemParameters.WorkArea.Width;
+        private double startingX;
+
+        public ZoomTool()
+        {
+            HideHighlight = true;
+            CanStartOutsideCanvas = true;
+            Tooltip = "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
+            pixelsPerZoomMultiplier = workAreaWidth / ZoomSensitivityMultiplier;
+        }
+
+        public override ToolType ToolType => ToolType.Zoom;
+
+        public override void OnMouseDown(MouseEventArgs e)
+        {
+            startingX = MousePositionConverter.GetCursorPosition().X;
+            ViewModelMain.Current.ZoomPercentage = 100; // This resest the value, so callback in MainDrawingPanel can fire again later
+        }
+
+        public override void OnMouseMove(MouseEventArgs e)
+        {
+            if (e.LeftButton == MouseButtonState.Pressed)
+            {
+                double xPos = MousePositionConverter.GetCursorPosition().X;
+
+                double rawPercentDifference = (xPos - startingX) / pixelsPerZoomMultiplier; // negative - zoom out, positive - zoom in, linear
+                double finalPercentDifference = Math.Pow(2, rawPercentDifference) * 100.0; // less than 100 - zoom out, greater than 100 - zoom in
+                Zoom(finalPercentDifference);
+            }
+        }
+
+        public override void OnMouseUp(MouseEventArgs e)
+        {
+            if (e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released &&
+                startingX == MousePositionConverter.GetCursorPosition().X)
+            {
                 if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
                 if (Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
                 {
                 {
                     Zoom(85);
                     Zoom(85);
@@ -54,16 +54,16 @@ namespace PixiEditor.Models.Tools.Tools
                 {
                 {
                     Zoom(115);
                     Zoom(115);
                 }
                 }
-            }
-        }
-
-        public void Zoom(double percentage)
-        {
-            ViewModelMain.Current.ZoomPercentage = percentage;
-        }
-
-        public override void Use(Coordinates[] pixels)
-        {
-        }
-    }
+            }
+        }
+
+        public void Zoom(double percentage)
+        {
+            ViewModelMain.Current.ZoomPercentage = percentage;
+        }
+
+        public override void Use(Coordinates[] pixels)
+        {
+        }
+    }
 }
 }

+ 15 - 14
PixiEditor/NotifyableObject.cs

@@ -1,19 +1,20 @@
-using System;
-using System.ComponentModel;
-
-namespace PixiEditor.Helpers
-{
-    [Serializable]
-    public class NotifyableObject : INotifyPropertyChanged
-    {
-        [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged = delegate { };
-
-        protected void RaisePropertyChanged(string property)
-        {
+using System;
+using System.ComponentModel;
+
+namespace PixiEditor.Helpers
+{
+    [Serializable]
+    public class NotifyableObject : INotifyPropertyChanged
+    {
+        [field: NonSerialized]
+        public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
+
+        protected void RaisePropertyChanged(string property)
+        {
             if (property != null)
             if (property != null)
             {
             {
                 PropertyChanged(this, new PropertyChangedEventArgs(property));
                 PropertyChanged(this, new PropertyChangedEventArgs(property));
             }
             }
-        }
-    }
+        }
+    }
 }
 }

+ 6 - 6
PixiEditor/Properties/AssemblyInfo.cs

@@ -27,14 +27,15 @@ using System.Windows;
 // the line below to match the UICulture setting in the project file.
 // the line below to match the UICulture setting in the project file.
 
 
 // [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
 // [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
-
 [assembly: ThemeInfo(
 [assembly: ThemeInfo(
     ResourceDictionaryLocation.None, // where theme specific resource dictionaries are located
     ResourceDictionaryLocation.None, // where theme specific resource dictionaries are located
-                                     // (used if a resource is not found in the page,
-                                     // or application resource dictionaries)
+
+    // (used if a resource is not found in the page,
+    // or application resource dictionaries)
     ResourceDictionaryLocation.SourceAssembly) // where the generic resource dictionary is located
     ResourceDictionaryLocation.SourceAssembly) // where the generic resource dictionary is located
-                                               // (used if a resource is not found in the page,
-                                               // app, or any theme specific resource dictionaries)
+
+// (used if a resource is not found in the page,
+// app, or any theme specific resource dictionaries)
 ]
 ]
 
 
 // Version information for an assembly consists of the following four values:
 // Version information for an assembly consists of the following four values:
@@ -47,6 +48,5 @@ using System.Windows;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-
 [assembly: AssemblyVersion("0.1.3.0")]
 [assembly: AssemblyVersion("0.1.3.0")]
 [assembly: AssemblyFileVersion("0.1.3.0")]
 [assembly: AssemblyFileVersion("0.1.3.0")]

+ 1 - 1
PixiEditor/ViewModels/ImportFilePopupViewModel.cs

@@ -102,7 +102,7 @@ namespace PixiEditor.ViewModels
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Command that handles Path choosing to save file
+        ///     Command that handles Path choosing to save file.
         /// </summary>
         /// </summary>
         /// <param name="parameter"></param>
         /// <param name="parameter"></param>
         private void ChoosePath(object parameter)
         private void ChoosePath(object parameter)

+ 1 - 1
PixiEditor/ViewModels/SaveFilePopupViewModel.cs

@@ -68,7 +68,7 @@ namespace PixiEditor.ViewModels
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Command that handles Path choosing to save file
+        ///     Command that handles Path choosing to save file.
         /// </summary>
         /// </summary>
         /// <param name="parameter"></param>
         /// <param name="parameter"></param>
         private void ChoosePath(object parameter)
         private void ChoosePath(object parameter)

+ 25 - 25
PixiEditor/ViewModels/ViewModelBase.cs

@@ -1,34 +1,34 @@
-using System.ComponentModel;
-using System.Linq;
-using System.Windows;
-using System.Windows.Input;
-
-namespace PixiEditor.ViewModels
-{
-    public class ViewModelBase : INotifyPropertyChanged
-    {
-        public event PropertyChangedEventHandler PropertyChanged = delegate { };
-
-        protected void RaisePropertyChanged(string property)
-        {
+using System.ComponentModel;
+using System.Linq;
+using System.Windows;
+using System.Windows.Input;
+
+namespace PixiEditor.ViewModels
+{
+    public class ViewModelBase : INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
+
+        protected void RaisePropertyChanged(string property)
+        {
             if (property != null)
             if (property != null)
             {
             {
                 PropertyChanged(this, new PropertyChangedEventArgs(property));
                 PropertyChanged(this, new PropertyChangedEventArgs(property));
             }
             }
-        }
-
-        protected void CloseButton(object parameter)
-        {
-            ((Window)parameter).Close();
-        }
-
-        protected void DragMove(object parameter)
-        {
-            Window popup = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
+        }
+
+        protected void CloseButton(object parameter)
+        {
+            ((Window)parameter).Close();
+        }
+
+        protected void DragMove(object parameter)
+        {
+            Window popup = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
             if (Mouse.LeftButton == MouseButtonState.Pressed)
             if (Mouse.LeftButton == MouseButtonState.Pressed)
             {
             {
                 popup.DragMove();
                 popup.DragMove();
             }
             }
-        }
-    }
+        }
+    }
 }
 }

+ 338 - 346
PixiEditor/ViewModels/ViewModelMain.cs

@@ -126,6 +126,7 @@ namespace PixiEditor.ViewModels
                     new Shortcut(Key.OemMinus, ZoomCommand, 85),
                     new Shortcut(Key.OemMinus, ZoomCommand, 85),
                     new Shortcut(Key.OemOpenBrackets, ChangeToolSizeCommand, -1),
                     new Shortcut(Key.OemOpenBrackets, ChangeToolSizeCommand, -1),
                     new Shortcut(Key.OemCloseBrackets, ChangeToolSizeCommand, 1),
                     new Shortcut(Key.OemCloseBrackets, ChangeToolSizeCommand, 1),
+
                     // Editor
                     // Editor
                     new Shortcut(Key.X, SwapColorsCommand),
                     new Shortcut(Key.X, SwapColorsCommand),
                     new Shortcut(Key.Y, RedoCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.Y, RedoCommand, modifier: ModifierKeys.Control),
@@ -140,9 +141,12 @@ namespace PixiEditor.ViewModels
                     new Shortcut(Key.I, OpenResizePopupCommand, modifier: ModifierKeys.Control | ModifierKeys.Shift),
                     new Shortcut(Key.I, OpenResizePopupCommand, modifier: ModifierKeys.Control | ModifierKeys.Shift),
                     new Shortcut(Key.C, OpenResizePopupCommand, "canvas", ModifierKeys.Control | ModifierKeys.Shift),
                     new Shortcut(Key.C, OpenResizePopupCommand, "canvas", ModifierKeys.Control | ModifierKeys.Shift),
                     new Shortcut(Key.F11, SystemCommands.MaximizeWindowCommand),
                     new Shortcut(Key.F11, SystemCommands.MaximizeWindowCommand),
+
                     // File
                     // File
                     new Shortcut(Key.O, OpenFileCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.O, OpenFileCommand, modifier: ModifierKeys.Control),
-                    new Shortcut(Key.S, ExportFileCommand,
+                    new Shortcut(
+                        Key.S,
+                        ExportFileCommand,
                         modifier: ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt),
                         modifier: ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt),
                     new Shortcut(Key.S, SaveDocumentCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.S, SaveDocumentCommand, modifier: ModifierKeys.Control),
                     new Shortcut(Key.S, SaveDocumentCommand, "AsNew", ModifierKeys.Control | ModifierKeys.Shift),
                     new Shortcut(Key.S, SaveDocumentCommand, "AsNew", ModifierKeys.Control | ModifierKeys.Shift),
@@ -157,10 +161,10 @@ namespace PixiEditor.ViewModels
             InitUpdateChecker();
             InitUpdateChecker();
         }
         }
 
 
-        public Action CloseAction { get; set; }
-
         public static ViewModelMain Current { get; set; }
         public static ViewModelMain Current { get; set; }
 
 
+        public Action CloseAction { get; set; }
+
         public RelayCommand SelectToolCommand { get; set; } // Command that handles tool switching
         public RelayCommand SelectToolCommand { get; set; } // Command that handles tool switching
 
 
         public RelayCommand OpenNewFilePopupCommand { get; set; } // Command that generates draw area
         public RelayCommand OpenNewFilePopupCommand { get; set; } // Command that generates draw area
@@ -363,12 +367,6 @@ namespace PixiEditor.ViewModels
 
 
         public UpdateChecker UpdateChecker { get; set; }
         public UpdateChecker UpdateChecker { get; set; }
 
 
-        private void RestartApplication(object parameter)
-        {
-            Process.Start(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "PixiEditor.UpdateInstaller.exe"));
-            Application.Current.Shutdown();
-        }
-
         public async Task<bool> CheckForUpdate()
         public async Task<bool> CheckForUpdate()
         {
         {
             return await Task.Run(async () =>
             return await Task.Run(async () =>
@@ -388,187 +386,6 @@ namespace PixiEditor.ViewModels
             });
             });
         }
         }
 
 
-        private void InitUpdateChecker()
-        {
-            Assembly assembly = Assembly.GetExecutingAssembly();
-            FileVersionInfo info = FileVersionInfo.GetVersionInfo(assembly.Location);
-            UpdateChecker = new UpdateChecker(info.FileVersion);
-            VersionText = $"Version {info.FileVersion}";
-        }
-
-        private void ZoomViewport(object parameter)
-        {
-            double zoom = (int)parameter;
-            ZoomPercentage = zoom;
-            ZoomPercentage = 100;
-        }
-
-        private void ChangeToolSize(object parameter)
-        {
-            int increment = (int)parameter;
-            int newSize = BitmapManager.ToolSize + increment;
-            if (newSize > 0)
-            {
-                BitmapManager.ToolSize = newSize;
-            }
-        }
-
-        private void OpenHyperlink(object parameter)
-        {
-            if (parameter == null)
-            {
-                return;
-            }
-
-            string url = (string)parameter;
-            ProcessStartInfo processInfo = new ProcessStartInfo
-            {
-                FileName = url,
-                UseShellExecute = true
-            };
-            Process.Start(processInfo);
-        }
-
-        private void CenterContent(object property)
-        {
-            BitmapManager.ActiveDocument.CenterContent();
-        }
-
-        private void CloseWindow(object property)
-        {
-            if (!(property is CancelEventArgs))
-            {
-                throw new ArgumentException();
-            } ((CancelEventArgs)property).Cancel = true;
-
-            ConfirmationType result = ConfirmationType.No;
-            if (unsavedDocumentModified)
-            {
-                result = ConfirmationDialog.Show(ConfirmationDialogMessage);
-                if (result == ConfirmationType.Yes)
-                {
-                    SaveDocument(null);
-                }
-            }
-
-            if (result != ConfirmationType.Canceled)
-            {
-                ((CancelEventArgs)property).Cancel = false;
-            }
-        }
-
-        private async void OnStartup(object parameter)
-        {
-            string lastArg = Environment.GetCommandLineArgs().Last();
-            if (Importer.IsSupportedFile(lastArg) && File.Exists(lastArg))
-            {
-                Open(lastArg);
-            }
-            else
-            {
-                OpenNewFilePopup(null);
-            }
-
-            await CheckForUpdate();
-        }
-
-        private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
-        {
-            e.NewDocument.DocumentSizeChanged += ActiveDocument_DocumentSizeChanged;
-        }
-
-        private void Open(object property)
-        {
-            OpenFileDialog dialog = new OpenFileDialog
-            {
-                Filter = "All Files|*.*|PixiEditor Files | *.pixi|PNG Files|*.png",
-                DefaultExt = "pixi"
-            };
-            if ((bool)dialog.ShowDialog())
-            {
-                if (Importer.IsSupportedFile(dialog.FileName))
-                {
-                    Open(dialog.FileName);
-                }
-
-                RecenterZoombox = !RecenterZoombox;
-            }
-        }
-
-        private void Open(string path)
-        {
-            if (unsavedDocumentModified)
-            {
-                ConfirmationType result = ConfirmationDialog.Show(ConfirmationDialogMessage);
-                if (result == ConfirmationType.Yes)
-                {
-                    SaveDocument(null);
-                }
-                else if (result == ConfirmationType.Canceled)
-                {
-                    return;
-                }
-            }
-
-            ResetProgramStateValues();
-            if (path.EndsWith(".pixi"))
-            {
-                OpenDocument(path);
-            }
-            else
-            {
-                OpenFile(path);
-            }
-        }
-
-        private void OpenDocument(string path)
-        {
-            BitmapManager.ActiveDocument = Importer.ImportDocument(path);
-            Exporter.SaveDocumentPath = path;
-            unsavedDocumentModified = false;
-        }
-
-        private void SaveDocument(object parameter)
-        {
-            bool paramIsAsNew = parameter != null && parameter.ToString()?.ToLower() == "asnew";
-            if (paramIsAsNew || Exporter.SaveDocumentPath == null)
-            {
-                bool saved = Exporter.SaveAsEditableFileWithDialog(BitmapManager.ActiveDocument, !paramIsAsNew);
-                unsavedDocumentModified = unsavedDocumentModified && !saved;
-            }
-            else
-            {
-                Exporter.SaveAsEditableFile(BitmapManager.ActiveDocument, Exporter.SaveDocumentPath);
-                unsavedDocumentModified = false;
-            }
-        }
-
-        private void RemoveSwatch(object parameter)
-        {
-            if (!(parameter is Color))
-            {
-                throw new ArgumentException();
-            }
-
-            Color color = (Color)parameter;
-            if (BitmapManager.ActiveDocument.Swatches.Contains(color))
-            {
-                BitmapManager.ActiveDocument.Swatches.Remove(color);
-            }
-        }
-
-        private void SelectColor(object parameter)
-        {
-            PrimaryColor = parameter as Color? ?? throw new ArgumentException();
-        }
-
-        private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
-        {
-            ActiveSelection = new Selection(Array.Empty<Coordinates>());
-            RecenterZoombox = !RecenterZoombox;
-            unsavedDocumentModified = true;
-        }
-
         public void AddSwatch(Color color)
         public void AddSwatch(Color color)
         {
         {
             if (!BitmapManager.ActiveDocument.Swatches.Contains(color))
             if (!BitmapManager.ActiveDocument.Swatches.Contains(color))
@@ -577,32 +394,6 @@ namespace PixiEditor.ViewModels
             }
             }
         }
         }
 
 
-        private void OpenResizePopup(object parameter)
-        {
-            bool isCanvasDialog = (string)parameter == "canvas";
-            ResizeDocumentDialog dialog = new ResizeDocumentDialog(
-                BitmapManager.ActiveDocument.Width,
-                BitmapManager.ActiveDocument.Height,
-                isCanvasDialog);
-            if (dialog.ShowDialog())
-            {
-                if (isCanvasDialog)
-                {
-                    BitmapManager.ActiveDocument.ResizeCanvas(dialog.Width, dialog.Height, dialog.ResizeAnchor);
-                }
-                else
-                {
-                    BitmapManager.ActiveDocument.Resize(dialog.Width, dialog.Height);
-                }
-            }
-        }
-
-        private void DeletePixels(object parameter)
-        {
-            BitmapManager.BitmapOperations.DeletePixels(new[] { BitmapManager.ActiveLayer },
-                ActiveSelection.SelectedPoints.ToArray());
-        }
-
         public void ClipCanvas(object parameter)
         public void ClipCanvas(object parameter)
         {
         {
             BitmapManager.ActiveDocument?.ClipCanvas();
             BitmapManager.ActiveDocument?.ClipCanvas();
@@ -618,7 +409,8 @@ namespace PixiEditor.ViewModels
         {
         {
             Copy(null);
             Copy(null);
             BitmapManager.ActiveLayer.SetPixels(
             BitmapManager.ActiveLayer.SetPixels(
-                BitmapPixelChanges.FromSingleColoredArray(ActiveSelection.SelectedPoints.ToArray(),
+                BitmapPixelChanges.FromSingleColoredArray(
+                    ActiveSelection.SelectedPoints.ToArray(),
                     Colors.Transparent));
                     Colors.Transparent));
         }
         }
 
 
@@ -627,28 +419,12 @@ namespace PixiEditor.ViewModels
             ClipboardController.PasteFromClipboard();
             ClipboardController.PasteFromClipboard();
         }
         }
 
 
-        private bool CanPaste(object property)
-        {
-            return DocumentIsNotNull(null) && ClipboardController.IsImageInClipboard();
-        }
-
-        private void Copy(object parameter)
-        {
-            ClipboardController.CopyToClipboard(BitmapManager.ActiveDocument.Layers.ToArray(),
-                ActiveSelection.SelectedPoints.ToArray(), BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height);
-        }
-
         public void SelectAll(object parameter)
         public void SelectAll(object parameter)
         {
         {
             SelectTool select = new SelectTool();
             SelectTool select = new SelectTool();
             ActiveSelection.SetSelection(select.GetAllSelection(), SelectionType.New);
             ActiveSelection.SetSelection(select.GetAllSelection(), SelectionType.New);
         }
         }
 
 
-        private bool CanSelectAll(object property)
-        {
-            return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 0;
-        }
-
         public bool DocumentIsNotNull(object property)
         public bool DocumentIsNotNull(object property)
         {
         {
             return BitmapManager.ActiveDocument != null;
             return BitmapManager.ActiveDocument != null;
@@ -659,11 +435,6 @@ namespace PixiEditor.ViewModels
             ActiveSelection?.Clear();
             ActiveSelection?.Clear();
         }
         }
 
 
-        private bool SelectionIsNotEmpty(object property)
-        {
-            return ActiveSelection?.SelectedPoints != null && ActiveSelection.SelectedPoints.Count > 0;
-        }
-
         public void SetTool(object parameter)
         public void SetTool(object parameter)
         {
         {
             SetActiveTool((ToolType)parameter);
             SetActiveTool((ToolType)parameter);
@@ -674,17 +445,6 @@ namespace PixiEditor.ViewModels
             BitmapManager.ActiveDocument.Layers[(int)parameter].IsRenaming = true;
             BitmapManager.ActiveDocument.Layers[(int)parameter].IsRenaming = true;
         }
         }
 
 
-        private void KeyUp(object parameter)
-        {
-            KeyEventArgs args = (KeyEventArgs)parameter;
-            if (restoreToolOnKeyUp && ShortcutController.LastShortcut != null && ShortcutController.LastShortcut.ShortcutKey == args.Key)
-            {
-                restoreToolOnKeyUp = false;
-                SetActiveTool(lastActionTool);
-                ShortcutController.BlockShortcutExecution = false;
-            }
-        }
-
         public void KeyDown(object parameter)
         public void KeyDown(object parameter)
         {
         {
             KeyEventArgs args = (KeyEventArgs)parameter;
             KeyEventArgs args = (KeyEventArgs)parameter;
@@ -697,11 +457,6 @@ namespace PixiEditor.ViewModels
             ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
             ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
         }
         }
 
 
-        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
-        {
-            TriggerNewUndoChange(BitmapManager.SelectedTool);
-        }
-
         public void TriggerNewUndoChange(Tool toolUsed)
         public void TriggerNewUndoChange(Tool toolUsed)
         {
         {
             if (BitmapManager.IsOperationTool(toolUsed)
             if (BitmapManager.IsOperationTool(toolUsed)
@@ -718,18 +473,7 @@ namespace PixiEditor.ViewModels
             }
             }
         }
         }
 
 
-        private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
-        {
-            ChangesController.AddChanges(new LayerChange(e.PixelsChanged, e.ChangedLayerIndex),
-                new LayerChange(e.OldPixelsValues, e.ChangedLayerIndex));
-            unsavedDocumentModified = true;
-            if (BitmapManager.IsOperationTool())
-            {
-                AddSwatch(PrimaryColor);
-            }
-        }
-
-        public void SwapColors(object parameter)
+        public void SwapColors(object parameter)
         {
         {
             Color tmp = PrimaryColor;
             Color tmp = PrimaryColor;
             PrimaryColor = SecondaryColor;
             PrimaryColor = SecondaryColor;
@@ -801,42 +545,6 @@ namespace PixiEditor.ViewModels
             SetToolCursor(tool.ToolType);
             SetToolCursor(tool.ToolType);
         }
         }
 
 
-        private void SetToolCursor(ToolType tool)
-        {
-            if (tool != ToolType.None)
-            {
-                ToolCursor = BitmapManager.SelectedTool.Cursor;
-            }
-            else
-            {
-                ToolCursor = Cursors.Arrow;
-            }
-        }
-
-        private void MouseDown(object parameter)
-        {
-            if (BitmapManager.ActiveDocument.Layers.Count == 0)
-            {
-                return;
-            }
-
-            if (Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                if (!BitmapManager.MouseController.IsRecordingChanges)
-                {
-                    bool clickedOnCanvas = MouseXOnCanvas >= 0 && MouseXOnCanvas <= BitmapManager.ActiveDocument.Width &&
-                                          MouseYOnCanvas >= 0 && MouseYOnCanvas <= BitmapManager.ActiveDocument.Height;
-                    BitmapManager.MouseController.StartRecordingMouseMovementChanges(clickedOnCanvas);
-                    BitmapManager.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
-                }
-            }
-
-            // Mouse down is guaranteed to only be raised from within this application, so by subscribing here we
-            // only listen for mouse up events that occurred as a result of a mouse down within this application.
-            // This seems better than maintaining a global listener indefinitely.
-            GlobalMouseHook.OnMouseUp += MouseHook_OnMouseUp;
-        }
-
         // this is public for testing.
         // this is public for testing.
         public void MouseHook_OnMouseUp(object sender, Point p)
         public void MouseHook_OnMouseUp(object sender, Point p)
         {
         {
@@ -845,26 +553,8 @@ namespace PixiEditor.ViewModels
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Method connected with command, it executes tool "activity"
-        /// </summary>
-        /// <param name="parameter"></param>
-        private void MouseMove(object parameter)
-        {
-            Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
-            MousePositionConverter.CurrentCoordinates = cords;
-
-            if (BitmapManager.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                BitmapManager.MouseController.RecordMouseMovementChange(cords);
-            }
-
-            BitmapManager.MouseController.MouseMoved(cords);
-        }
-
-        /// <summary>
-        ///     Generates new Layer and sets it as active one
+        ///     Generates new Layer and sets it as active one.
         /// </summary>
         /// </summary>
-        /// <param name="parameter"></param>
         public void OpenNewFilePopup(object parameter)
         public void OpenNewFilePopup(object parameter)
         {
         {
             NewFileDialog newFile = new NewFileDialog();
             NewFileDialog newFile = new NewFileDialog();
@@ -877,7 +567,6 @@ namespace PixiEditor.ViewModels
         /// <summary>
         /// <summary>
         ///     Opens file from path.
         ///     Opens file from path.
         /// </summary>
         /// </summary>
-        /// <param name="path"></param>
         public void OpenFile(string path)
         public void OpenFile(string path)
         {
         {
             ImportFileDialog dialog = new ImportFileDialog();
             ImportFileDialog dialog = new ImportFileDialog();
@@ -929,12 +618,9 @@ namespace PixiEditor.ViewModels
             return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 0;
             return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 0;
         }
         }
 
 
-        #region Undo/Redo
-
         /// <summary>
         /// <summary>
-        ///     Undo last action
+        ///     Undo last action.
         /// </summary>
         /// </summary>
-        /// <param name="parameter"></param>
         public void Undo(object parameter)
         public void Undo(object parameter)
         {
         {
             Deselect(null);
             Deselect(null);
@@ -942,42 +628,352 @@ namespace PixiEditor.ViewModels
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Returns true if undo can be done.
+        ///     Redo last action.
         /// </summary>
         /// </summary>
-        /// <param name="property"></param>
-        /// <returns></returns>
-        private bool CanUndo(object property)
+        public void Redo(object parameter)
         {
         {
-            return UndoManager.CanUndo;
+            UndoManager.Redo();
+        }
+
+        private void RestartApplication(object parameter)
+        {
+            Process.Start(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "PixiEditor.UpdateInstaller.exe"));
+            Application.Current.Shutdown();
+        }
+
+        private void InitUpdateChecker()
+        {
+            Assembly assembly = Assembly.GetExecutingAssembly();
+            FileVersionInfo info = FileVersionInfo.GetVersionInfo(assembly.Location);
+            UpdateChecker = new UpdateChecker(info.FileVersion);
+            VersionText = $"Version {info.FileVersion}";
+        }
+
+        private void ZoomViewport(object parameter)
+        {
+            double zoom = (int)parameter;
+            ZoomPercentage = zoom;
+            ZoomPercentage = 100;
+        }
+
+        private void ChangeToolSize(object parameter)
+        {
+            int increment = (int)parameter;
+            int newSize = BitmapManager.ToolSize + increment;
+            if (newSize > 0)
+            {
+                BitmapManager.ToolSize = newSize;
+            }
+        }
+
+        private void OpenHyperlink(object parameter)
+        {
+            if (parameter == null)
+            {
+                return;
+            }
+
+            string url = (string)parameter;
+            ProcessStartInfo processInfo = new ProcessStartInfo
+            {
+                FileName = url,
+                UseShellExecute = true
+            };
+            Process.Start(processInfo);
+        }
+
+        private void CenterContent(object property)
+        {
+            BitmapManager.ActiveDocument.CenterContent();
+        }
+
+        private void CloseWindow(object property)
+        {
+            if (!(property is CancelEventArgs))
+            {
+                throw new ArgumentException();
+            }
+
+            ((CancelEventArgs)property).Cancel = true;
+
+            ConfirmationType result = ConfirmationType.No;
+            if (unsavedDocumentModified)
+            {
+                result = ConfirmationDialog.Show(ConfirmationDialogMessage);
+                if (result == ConfirmationType.Yes)
+                {
+                    SaveDocument(null);
+                }
+            }
+
+            if (result != ConfirmationType.Canceled)
+            {
+                ((CancelEventArgs)property).Cancel = false;
+            }
+        }
+
+        private async void OnStartup(object parameter)
+        {
+            string lastArg = Environment.GetCommandLineArgs().Last();
+            if (Importer.IsSupportedFile(lastArg) && File.Exists(lastArg))
+            {
+                Open(lastArg);
+            }
+            else
+            {
+                OpenNewFilePopup(null);
+            }
+
+            await CheckForUpdate();
+        }
+
+        private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
+        {
+            e.NewDocument.DocumentSizeChanged += ActiveDocument_DocumentSizeChanged;
+        }
+
+        private void Open(object property)
+        {
+            OpenFileDialog dialog = new OpenFileDialog
+            {
+                Filter = "All Files|*.*|PixiEditor Files | *.pixi|PNG Files|*.png",
+                DefaultExt = "pixi"
+            };
+            if ((bool)dialog.ShowDialog())
+            {
+                if (Importer.IsSupportedFile(dialog.FileName))
+                {
+                    Open(dialog.FileName);
+                }
+
+                RecenterZoombox = !RecenterZoombox;
+            }
+        }
+
+        private void Open(string path)
+        {
+            if (unsavedDocumentModified)
+            {
+                ConfirmationType result = ConfirmationDialog.Show(ConfirmationDialogMessage);
+                if (result == ConfirmationType.Yes)
+                {
+                    SaveDocument(null);
+                }
+                else if (result == ConfirmationType.Canceled)
+                {
+                    return;
+                }
+            }
+
+            ResetProgramStateValues();
+            if (path.EndsWith(".pixi"))
+            {
+                OpenDocument(path);
+            }
+            else
+            {
+                OpenFile(path);
+            }
+        }
+
+        private void OpenDocument(string path)
+        {
+            BitmapManager.ActiveDocument = Importer.ImportDocument(path);
+            Exporter.SaveDocumentPath = path;
+            unsavedDocumentModified = false;
+        }
+
+        private void SaveDocument(object parameter)
+        {
+            bool paramIsAsNew = parameter != null && parameter.ToString()?.ToLower() == "asnew";
+            if (paramIsAsNew || Exporter.SaveDocumentPath == null)
+            {
+                bool saved = Exporter.SaveAsEditableFileWithDialog(BitmapManager.ActiveDocument, !paramIsAsNew);
+                unsavedDocumentModified = unsavedDocumentModified && !saved;
+            }
+            else
+            {
+                Exporter.SaveAsEditableFile(BitmapManager.ActiveDocument, Exporter.SaveDocumentPath);
+                unsavedDocumentModified = false;
+            }
+        }
+
+        private void RemoveSwatch(object parameter)
+        {
+            if (!(parameter is Color))
+            {
+                throw new ArgumentException();
+            }
+
+            Color color = (Color)parameter;
+            if (BitmapManager.ActiveDocument.Swatches.Contains(color))
+            {
+                BitmapManager.ActiveDocument.Swatches.Remove(color);
+            }
+        }
+
+        private void SelectColor(object parameter)
+        {
+            PrimaryColor = parameter as Color? ?? throw new ArgumentException();
+        }
+
+        private void ActiveDocument_DocumentSizeChanged(object sender, DocumentSizeChangedEventArgs e)
+        {
+            ActiveSelection = new Selection(Array.Empty<Coordinates>());
+            RecenterZoombox = !RecenterZoombox;
+            unsavedDocumentModified = true;
+        }
+
+        private void OpenResizePopup(object parameter)
+        {
+            bool isCanvasDialog = (string)parameter == "canvas";
+            ResizeDocumentDialog dialog = new ResizeDocumentDialog(
+                BitmapManager.ActiveDocument.Width,
+                BitmapManager.ActiveDocument.Height,
+                isCanvasDialog);
+            if (dialog.ShowDialog())
+            {
+                if (isCanvasDialog)
+                {
+                    BitmapManager.ActiveDocument.ResizeCanvas(dialog.Width, dialog.Height, dialog.ResizeAnchor);
+                }
+                else
+                {
+                    BitmapManager.ActiveDocument.Resize(dialog.Width, dialog.Height);
+                }
+            }
+        }
+
+        private void DeletePixels(object parameter)
+        {
+            BitmapManager.BitmapOperations.DeletePixels(
+                new[] { BitmapManager.ActiveLayer },
+                ActiveSelection.SelectedPoints.ToArray());
+        }
+
+        private bool CanPaste(object property)
+        {
+            return DocumentIsNotNull(null) && ClipboardController.IsImageInClipboard();
+        }
+
+        private void Copy(object parameter)
+        {
+            ClipboardController.CopyToClipboard(
+                BitmapManager.ActiveDocument.Layers.ToArray(),
+                ActiveSelection.SelectedPoints.ToArray(),
+                BitmapManager.ActiveDocument.Width,
+                BitmapManager.ActiveDocument.Height);
+        }
+
+        private bool SelectionIsNotEmpty(object property)
+        {
+            return ActiveSelection?.SelectedPoints != null && ActiveSelection.SelectedPoints.Count > 0;
+        }
+
+        private bool CanSelectAll(object property)
+        {
+            return BitmapManager.ActiveDocument != null && BitmapManager.ActiveDocument.Layers.Count > 0;
+        }
+
+        private void KeyUp(object parameter)
+        {
+            KeyEventArgs args = (KeyEventArgs)parameter;
+            if (restoreToolOnKeyUp && ShortcutController.LastShortcut != null && ShortcutController.LastShortcut.ShortcutKey == args.Key)
+            {
+                restoreToolOnKeyUp = false;
+                SetActiveTool(lastActionTool);
+                ShortcutController.BlockShortcutExecution = false;
+            }
+        }
+
+        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
+        {
+            TriggerNewUndoChange(BitmapManager.SelectedTool);
+        }
+
+        private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
+        {
+            ChangesController.AddChanges(
+                new LayerChange(e.PixelsChanged, e.ChangedLayerIndex),
+                new LayerChange(e.OldPixelsValues, e.ChangedLayerIndex));
+            unsavedDocumentModified = true;
+            if (BitmapManager.IsOperationTool())
+            {
+                AddSwatch(PrimaryColor);
+            }
+        }
+
+        private void SetToolCursor(ToolType tool)
+        {
+            if (tool != ToolType.None)
+            {
+                ToolCursor = BitmapManager.SelectedTool.Cursor;
+            }
+            else
+            {
+                ToolCursor = Cursors.Arrow;
+            }
+        }
+
+        private void MouseDown(object parameter)
+        {
+            if (BitmapManager.ActiveDocument.Layers.Count == 0)
+            {
+                return;
+            }
+
+            if (Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                if (!BitmapManager.MouseController.IsRecordingChanges)
+                {
+                    bool clickedOnCanvas = MouseXOnCanvas >= 0 && MouseXOnCanvas <= BitmapManager.ActiveDocument.Width &&
+                                          MouseYOnCanvas >= 0 && MouseYOnCanvas <= BitmapManager.ActiveDocument.Height;
+                    BitmapManager.MouseController.StartRecordingMouseMovementChanges(clickedOnCanvas);
+                    BitmapManager.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
+                }
+            }
+
+            // Mouse down is guaranteed to only be raised from within this application, so by subscribing here we
+            // only listen for mouse up events that occurred as a result of a mouse down within this application.
+            // This seems better than maintaining a global listener indefinitely.
+            GlobalMouseHook.OnMouseUp += MouseHook_OnMouseUp;
         }
         }
 
 
         /// <summary>
         /// <summary>
-        ///     Redo last action
+        ///     Method connected with command, it executes tool "activity".
         /// </summary>
         /// </summary>
-        /// <param name="parameter"></param>
-        public void Redo(object parameter)
+        private void MouseMove(object parameter)
         {
         {
-            UndoManager.Redo();
+            Coordinates cords = new Coordinates((int)MouseXOnCanvas, (int)MouseYOnCanvas);
+            MousePositionConverter.CurrentCoordinates = cords;
+
+            if (BitmapManager.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
+            {
+                BitmapManager.MouseController.RecordMouseMovementChange(cords);
+            }
+
+            BitmapManager.MouseController.MouseMoved(cords);
+        }
+
+        /// <summary>
+        ///     Returns true if undo can be done.
+        /// </summary>
+        private bool CanUndo(object property)
+        {
+            return UndoManager.CanUndo;
         }
         }
 
 
         /// <summary>
         /// <summary>
         ///     Returns true if redo can be done.
         ///     Returns true if redo can be done.
         /// </summary>
         /// </summary>
-        /// <param name="property"></param>
-        /// <returns></returns>
         private bool CanRedo(object property)
         private bool CanRedo(object property)
         {
         {
             return UndoManager.CanRedo;
             return UndoManager.CanRedo;
         }
         }
 
 
-        #endregion
-
-        #region SaveFile
-
         /// <summary>
         /// <summary>
         ///     Generates export dialog or saves directly if save data is known.
         ///     Generates export dialog or saves directly if save data is known.
         /// </summary>
         /// </summary>
-        /// <param name="parameter"></param>
         private void ExportFile(object parameter)
         private void ExportFile(object parameter)
         {
         {
             System.Windows.Media.Imaging.WriteableBitmap bitmap = BitmapManager.GetCombinedLayersBitmap();
             System.Windows.Media.Imaging.WriteableBitmap bitmap = BitmapManager.GetCombinedLayersBitmap();
@@ -987,13 +983,9 @@ namespace PixiEditor.ViewModels
         /// <summary>
         /// <summary>
         ///     Returns true if file save is possible.
         ///     Returns true if file save is possible.
         /// </summary>
         /// </summary>
-        /// <param name="property"></param>
-        /// <returns></returns>
         private bool CanSave(object property)
         private bool CanSave(object property)
         {
         {
             return BitmapManager.ActiveDocument != null;
             return BitmapManager.ActiveDocument != null;
         }
         }
-
-        #endregion
     }
     }
 }
 }

+ 39 - 40
PixiEditor/Views/AnchorPointPicker.xaml.cs

@@ -1,51 +1,50 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using PixiEditor.Models.Enums;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for AnchorPointPicker.xaml
-    /// </summary>
-    public partial class AnchorPointPicker : UserControl
-    {
-        // Using a DependencyProperty as the backing store for AnchorPoint.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty AnchorPointProperty =
-            DependencyProperty.Register("AnchorPoint", typeof(AnchorPoint), typeof(AnchorPointPicker),
-                new PropertyMetadata());
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using PixiEditor.Models.Enums;
 
 
-        private ToggleButton selectedToggleButton;
-
-        public AnchorPointPicker()
-        {
-            InitializeComponent();
-        }
-
-        public AnchorPoint AnchorPoint
-        {
-            get => (AnchorPoint)GetValue(AnchorPointProperty);
-            set => SetValue(AnchorPointProperty, value);
-        }
-
-        private void ToggleButton_Checked(object sender, RoutedEventArgs e)
-        {
-            ToggleButton btn = (ToggleButton)sender;
-            AnchorPoint = (AnchorPoint)(1 << (Grid.GetRow(btn) + 3)) | (AnchorPoint)(1 << Grid.GetColumn(btn));
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for AnchorPointPicker.xaml.
+    /// </summary>
+    public partial class AnchorPointPicker : UserControl
+    {
+        // Using a DependencyProperty as the backing store for AnchorPoint.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty AnchorPointProperty =
+            DependencyProperty.Register("AnchorPoint", typeof(AnchorPoint), typeof(AnchorPointPicker), new PropertyMetadata());
+
+        private ToggleButton selectedToggleButton;
+
+        public AnchorPointPicker()
+        {
+            InitializeComponent();
+        }
+
+        public AnchorPoint AnchorPoint
+        {
+            get => (AnchorPoint)GetValue(AnchorPointProperty);
+            set => SetValue(AnchorPointProperty, value);
+        }
+
+        private void ToggleButton_Checked(object sender, RoutedEventArgs e)
+        {
+            ToggleButton btn = (ToggleButton)sender;
+            AnchorPoint = (AnchorPoint)(1 << (Grid.GetRow(btn) + 3)) | (AnchorPoint)(1 << Grid.GetColumn(btn));
             if (selectedToggleButton != null)
             if (selectedToggleButton != null)
             {
             {
                 selectedToggleButton.IsChecked = false;
                 selectedToggleButton.IsChecked = false;
             }
             }
 
 
-            selectedToggleButton = btn;
-        }
-
-        private void ToggleButton_Click(object sender, RoutedEventArgs e)
-        {
+            selectedToggleButton = btn;
+        }
+
+        private void ToggleButton_Click(object sender, RoutedEventArgs e)
+        {
             if ((sender as ToggleButton).IsChecked.Value)
             if ((sender as ToggleButton).IsChecked.Value)
             {
             {
                 e.Handled = true;
                 e.Handled = true;
             }
             }
-        }
-    }
+        }
+    }
 }
 }

+ 60 - 57
PixiEditor/Views/ConfirmationPopup.xaml.cs

@@ -1,58 +1,61 @@
-using System.Windows;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for ConfirmationPopup.xaml
-    /// </summary>
-    public partial class ConfirmationPopup : Window
-    {
-        // Using a DependencyProperty as the backing store for SaveChanges.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty SaveChangesProperty =
-            DependencyProperty.Register("SaveChanges", typeof(bool), typeof(ConfirmationPopup),
-                new PropertyMetadata(true));
-
-        // Using a DependencyProperty as the backing store for Body.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty BodyProperty =
-            DependencyProperty.Register("Body", typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(""));
-
-        public ConfirmationPopup()
-        {
-            InitializeComponent();
-            CancelCommand = new RelayCommand(Cancel);
-            SetResultAndCloseCommand = new RelayCommand(SetResultAndClose);
-            DataContext = this;
-        }
-
-        public RelayCommand CancelCommand { get; set; }
-
-        public RelayCommand SetResultAndCloseCommand { get; set; }
-
-        public bool Result
-        {
-            get => (bool)GetValue(SaveChangesProperty);
-            set => SetValue(SaveChangesProperty, value);
-        }
-
-        public string Body
-        {
-            get => (string)GetValue(BodyProperty);
-            set => SetValue(BodyProperty, value);
-        }
-
-        private void SetResultAndClose(object property)
-        {
-            bool result = (bool)property;
-            Result = result;
-            DialogResult = true;
-            Close();
-        }
-
-        private void Cancel(object property)
-        {
-            DialogResult = false;
-            Close();
-        }
-    }
+using System.Windows;
+using PixiEditor.Helpers;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for ConfirmationPopup.xaml.
+    /// </summary>
+    public partial class ConfirmationPopup : Window
+    {
+        // Using a DependencyProperty as the backing store for SaveChanges.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty SaveChangesProperty =
+            DependencyProperty.Register(
+                "SaveChanges",
+                typeof(bool),
+                typeof(ConfirmationPopup),
+                new PropertyMetadata(true));
+
+        // Using a DependencyProperty as the backing store for Body.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty BodyProperty =
+            DependencyProperty.Register("Body", typeof(string), typeof(ConfirmationPopup), new PropertyMetadata(string.Empty));
+
+        public ConfirmationPopup()
+        {
+            InitializeComponent();
+            CancelCommand = new RelayCommand(Cancel);
+            SetResultAndCloseCommand = new RelayCommand(SetResultAndClose);
+            DataContext = this;
+        }
+
+        public RelayCommand CancelCommand { get; set; }
+
+        public RelayCommand SetResultAndCloseCommand { get; set; }
+
+        public bool Result
+        {
+            get => (bool)GetValue(SaveChangesProperty);
+            set => SetValue(SaveChangesProperty, value);
+        }
+
+        public string Body
+        {
+            get => (string)GetValue(BodyProperty);
+            set => SetValue(BodyProperty, value);
+        }
+
+        private void SetResultAndClose(object property)
+        {
+            bool result = (bool)property;
+            Result = result;
+            DialogResult = true;
+            Close();
+        }
+
+        private void Cancel(object property)
+        {
+            DialogResult = false;
+            Close();
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/EditableTextBlock.xaml

@@ -17,7 +17,7 @@
                  LostFocus="TextBox_LostFocus"
                  LostFocus="TextBox_LostFocus"
                  Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
                  Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
                  KeyDown="TextBox_KeyDown"
                  KeyDown="TextBox_KeyDown"
-                 LostKeyboardFocus="textBox_LostKeyboardFocus"
+                 LostKeyboardFocus="TextBox_LostKeyboardFocus"
                  Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
                  Visibility="{Binding Path=TextBlockVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
             Converter={StaticResource OppositeVisibilityConverter}}"
             Converter={StaticResource OppositeVisibilityConverter}}"
                  Name="textBox" />
                  Name="textBox" />

+ 14 - 17
PixiEditor/Views/EditableTextBlock.xaml.cs

@@ -6,24 +6,21 @@ using PixiEditor.Models.Controllers.Shortcuts;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for EditableTextBlock.xaml
+    ///     Interaction logic for EditableTextBlock.xaml.
     /// </summary>
     /// </summary>
     public partial class EditableTextBlock : UserControl
     public partial class EditableTextBlock : UserControl
     {
     {
         // Using a DependencyProperty as the backing store for TextBlockVisibility.  This enables animation, styling, binding, etc...
         // Using a DependencyProperty as the backing store for TextBlockVisibility.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty TextBlockVisibilityProperty =
         public static readonly DependencyProperty TextBlockVisibilityProperty =
-            DependencyProperty.Register("TextBlockVisibility", typeof(Visibility), typeof(EditableTextBlock),
-                new PropertyMetadata(Visibility.Visible));
+            DependencyProperty.Register("TextBlockVisibility", typeof(Visibility), typeof(EditableTextBlock), new PropertyMetadata(Visibility.Visible));
 
 
         // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
         // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty TextProperty =
         public static readonly DependencyProperty TextProperty =
-            DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBlock),
-                new PropertyMetadata(default(string)));
+            DependencyProperty.Register("Text", typeof(string), typeof(EditableTextBlock), new PropertyMetadata(default(string)));
 
 
         // Using a DependencyProperty as the backing store for EnableEditing.  This enables animation, styling, binding, etc...
         // Using a DependencyProperty as the backing store for EnableEditing.  This enables animation, styling, binding, etc...
         public static readonly DependencyProperty EnableEditingProperty =
         public static readonly DependencyProperty EnableEditingProperty =
-            DependencyProperty.Register("IsEditing", typeof(bool), typeof(EditableTextBlock),
-                new PropertyMetadata(OnIsEditingChanged));
+            DependencyProperty.Register("IsEditing", typeof(bool), typeof(EditableTextBlock), new PropertyMetadata(OnIsEditingChanged));
 
 
         public EditableTextBlock()
         public EditableTextBlock()
         {
         {
@@ -48,15 +45,6 @@ namespace PixiEditor.Views
             set => SetValue(TextProperty, value);
             set => SetValue(TextProperty, value);
         }
         }
 
 
-        private static void OnIsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            if ((bool)e.NewValue)
-            {
-                EditableTextBlock tb = (EditableTextBlock)d;
-                tb.EnableEditing();
-            }
-        }
-
         public void EnableEditing()
         public void EnableEditing()
         {
         {
             ShortcutController.BlockShortcutExecution = true;
             ShortcutController.BlockShortcutExecution = true;
@@ -66,6 +54,15 @@ namespace PixiEditor.Views
             textBox.SelectAll();
             textBox.SelectAll();
         }
         }
 
 
+        private static void OnIsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            if ((bool)e.NewValue)
+            {
+                EditableTextBlock tb = (EditableTextBlock)d;
+                tb.EnableEditing();
+            }
+        }
+
         private void DisableEditing()
         private void DisableEditing()
         {
         {
             TextBlockVisibility = Visibility.Visible;
             TextBlockVisibility = Visibility.Visible;
@@ -94,7 +91,7 @@ namespace PixiEditor.Views
             DisableEditing();
             DisableEditing();
         }
         }
 
 
-        private void textBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
+        private void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
         {
         {
             DisableEditing();
             DisableEditing();
         }
         }

+ 1 - 1
PixiEditor/Views/ImportFilePopup.xaml.cs

@@ -4,7 +4,7 @@ using PixiEditor.ViewModels;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for ImportFilePopup.xaml
+    ///     Interaction logic for ImportFilePopup.xaml.
     /// </summary>
     /// </summary>
     public partial class ImportFilePopup : Window
     public partial class ImportFilePopup : Window
     {
     {

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

@@ -6,7 +6,7 @@ using PixiEditor.Helpers;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for LayerItem.xaml
+    ///     Interaction logic for LayerItem.xaml.
     /// </summary>
     /// </summary>
     public partial class LayerItem : UserControl
     public partial class LayerItem : UserControl
     {
     {

+ 1 - 1
PixiEditor/Views/MainDrawingPanel.xaml

@@ -5,7 +5,7 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
              xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
              xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
              xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
-             mc:Ignorable="d" PreviewMouseDown="mainDrawingPanel_MouseDown" PreviewMouseUp="mainDrawingPanel_PreviewMouseUp"
+             mc:Ignorable="d" PreviewMouseDown="MainDrawingPanel_MouseDown" PreviewMouseUp="MainDrawingPanel_PreviewMouseUp"
              d:DesignHeight="450" d:DesignWidth="800" x:Name="mainDrawingPanel" PreviewMouseWheel="Zoombox_MouseWheel">
              d:DesignHeight="450" d:DesignWidth="800" x:Name="mainDrawingPanel" PreviewMouseWheel="Zoombox_MouseWheel">
     <xctk:Zoombox PreviewMouseDown="Zoombox_PreviewMouseDown" Cursor="{Binding Cursor}" Name="Zoombox" KeepContentInBounds="True"
     <xctk:Zoombox PreviewMouseDown="Zoombox_PreviewMouseDown" Cursor="{Binding Cursor}" Name="Zoombox" KeepContentInBounds="True"
                   Loaded="Zoombox_Loaded" IsAnimated="False" CurrentViewChanged="Zoombox_CurrentViewChanged" DragModifiers="Shift" ZoomModifiers="None">
                   Loaded="Zoombox_Loaded" IsAnimated="False" CurrentViewChanged="Zoombox_CurrentViewChanged" DragModifiers="Shift" ZoomModifiers="None">

+ 162 - 165
PixiEditor/Views/MainDrawingPanel.xaml.cs

@@ -1,190 +1,187 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using PixiEditor.Models.Tools.Tools;
-using PixiEditor.ViewModels;
-using Xceed.Wpf.Toolkit.Core.Input;
-using Xceed.Wpf.Toolkit.Zoombox;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for MainDrawingPanel.xaml
-    /// </summary>
-    public partial class MainDrawingPanel : UserControl
-    {
-        // Using a DependencyProperty as the backing store for Center.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty CenterProperty =
-            DependencyProperty.Register("Center", typeof(bool), typeof(MainDrawingPanel),
-                new PropertyMetadata(true, OnCenterChanged));
-
-        // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseXProperty =
-            DependencyProperty.Register("MouseX", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(null));
-
-        // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseYProperty =
-            DependencyProperty.Register("MouseY", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(null));
-
-        // Using a DependencyProperty as the backing store for MouseMoveCommand.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MouseMoveCommandProperty =
-            DependencyProperty.Register("MouseMoveCommand", typeof(ICommand), typeof(MainDrawingPanel),
-                new PropertyMetadata(null));
-
-        // Using a DependencyProperty as the backing store for CenterOnStart.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty CenterOnStartProperty =
-            DependencyProperty.Register("CenterOnStart", typeof(bool), typeof(MainDrawingPanel),
-                new PropertyMetadata(false));
-
-        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ItemProperty =
-            DependencyProperty.Register("Item", typeof(object), typeof(MainDrawingPanel), new PropertyMetadata(default(FrameworkElement)));
-
-        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty IsUsingZoomToolProperty =
-            DependencyProperty.Register("IsUsingZoomTool", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(false));
-
-        // Using a DependencyProperty as the backing store for ZoomPercentage.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ZoomPercentageProperty =
-            DependencyProperty.Register("ZoomPercentage", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(0.0, ZoomPercentegeChanged));
-
-        public double ClickScale;
-
-        public MainDrawingPanel()
-        {
-            InitializeComponent();
-            Zoombox.ZoomToSelectionModifiers = new KeyModifierCollection { KeyModifier.RightAlt };
-        }
-
-        public double ZoomPercentage
-        {
-            get => (double)GetValue(ZoomPercentageProperty);
-            set => SetValue(ZoomPercentageProperty, value);
-        }
-
-        public bool Center
-        {
-            get => (bool)GetValue(CenterProperty);
-            set => SetValue(CenterProperty, value);
-        }
-
-        public double MouseX
-        {
-            get => (double)GetValue(MouseXProperty);
-            set => SetValue(MouseXProperty, value);
-        }
-
-        public double MouseY
-        {
-            get => (double)GetValue(MouseYProperty);
-            set => SetValue(MouseYProperty, value);
-        }
-
-        public ICommand MouseMoveCommand
-        {
-            get => (ICommand)GetValue(MouseMoveCommandProperty);
-            set => SetValue(MouseMoveCommandProperty, value);
-        }
-
-        public bool CenterOnStart
-        {
-            get => (bool)GetValue(CenterOnStartProperty);
-            set => SetValue(CenterOnStartProperty, value);
-        }
-
-        public object Item
-        {
-            get => GetValue(ItemProperty);
-            set => SetValue(ItemProperty, value);
-        }
-
-        public bool IsUsingZoomTool
-        {
-            get => (bool)GetValue(IsUsingZoomToolProperty);
-            set => SetValue(IsUsingZoomToolProperty, value);
-        }
-
-        private static void ZoomPercentegeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            MainDrawingPanel panel = (MainDrawingPanel)d;
-            double percentage = (double)e.NewValue;
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using PixiEditor.Models.Tools.Tools;
+using PixiEditor.ViewModels;
+using Xceed.Wpf.Toolkit.Core.Input;
+using Xceed.Wpf.Toolkit.Zoombox;
+
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for MainDrawingPanel.xaml.
+    /// </summary>
+    public partial class MainDrawingPanel : UserControl
+    {
+        // Using a DependencyProperty as the backing store for Center.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty CenterProperty =
+            DependencyProperty.Register("Center", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(true, OnCenterChanged));
+
+        // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MouseXProperty =
+            DependencyProperty.Register("MouseX", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(null));
+
+        // Using a DependencyProperty as the backing store for MouseX.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MouseYProperty =
+            DependencyProperty.Register("MouseY", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(null));
+
+        // Using a DependencyProperty as the backing store for MouseMoveCommand.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MouseMoveCommandProperty =
+            DependencyProperty.Register("MouseMoveCommand", typeof(ICommand), typeof(MainDrawingPanel), new PropertyMetadata(null));
+
+        // Using a DependencyProperty as the backing store for CenterOnStart.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty CenterOnStartProperty =
+            DependencyProperty.Register("CenterOnStart", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(false));
+
+        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ItemProperty =
+            DependencyProperty.Register("Item", typeof(object), typeof(MainDrawingPanel), new PropertyMetadata(default(FrameworkElement)));
+
+        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty IsUsingZoomToolProperty =
+            DependencyProperty.Register("IsUsingZoomTool", typeof(bool), typeof(MainDrawingPanel), new PropertyMetadata(false));
+
+        // Using a DependencyProperty as the backing store for ZoomPercentage.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ZoomPercentageProperty =
+            DependencyProperty.Register("ZoomPercentage", typeof(double), typeof(MainDrawingPanel), new PropertyMetadata(0.0, ZoomPercentegeChanged));
+
+        public MainDrawingPanel()
+        {
+            InitializeComponent();
+            Zoombox.ZoomToSelectionModifiers = new KeyModifierCollection { KeyModifier.RightAlt };
+        }
+
+        public double ClickScale { get; set; }
+
+        public double ZoomPercentage
+        {
+            get => (double)GetValue(ZoomPercentageProperty);
+            set => SetValue(ZoomPercentageProperty, value);
+        }
+
+        public bool Center
+        {
+            get => (bool)GetValue(CenterProperty);
+            set => SetValue(CenterProperty, value);
+        }
+
+        public double MouseX
+        {
+            get => (double)GetValue(MouseXProperty);
+            set => SetValue(MouseXProperty, value);
+        }
+
+        public double MouseY
+        {
+            get => (double)GetValue(MouseYProperty);
+            set => SetValue(MouseYProperty, value);
+        }
+
+        public ICommand MouseMoveCommand
+        {
+            get => (ICommand)GetValue(MouseMoveCommandProperty);
+            set => SetValue(MouseMoveCommandProperty, value);
+        }
+
+        public bool CenterOnStart
+        {
+            get => (bool)GetValue(CenterOnStartProperty);
+            set => SetValue(CenterOnStartProperty, value);
+        }
+
+        public object Item
+        {
+            get => GetValue(ItemProperty);
+            set => SetValue(ItemProperty, value);
+        }
+
+        public bool IsUsingZoomTool
+        {
+            get => (bool)GetValue(IsUsingZoomToolProperty);
+            set => SetValue(IsUsingZoomToolProperty, value);
+        }
+
+        private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            MainDrawingPanel panel = (MainDrawingPanel)d;
+            panel.Zoombox.CenterContent();
+        }
+
+        private static void ZoomPercentegeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            MainDrawingPanel panel = (MainDrawingPanel)d;
+            double percentage = (double)e.NewValue;
             if (percentage == 100)
             if (percentage == 100)
             {
             {
                 panel.SetClickValues();
                 panel.SetClickValues();
             }
             }
 
 
-            panel.Zoombox.ZoomTo(panel.ClickScale * ((double)e.NewValue / 100.0));
-        }
-
-        private void Zoombox_CurrentViewChanged(object sender, ZoomboxViewChangedEventArgs e)
-        {
-            Zoombox.MinScale = 32 / ((FrameworkElement)Item).Width;
-            Zoombox.KeepContentInBounds = !(Zoombox.Scale > Zoombox.MinScale * 35.0);
-        }
-
-        private void Zoombox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
-        {
+            panel.Zoombox.ZoomTo(panel.ClickScale * ((double)e.NewValue / 100.0));
+        }
+
+        private void Zoombox_CurrentViewChanged(object sender, ZoomboxViewChangedEventArgs e)
+        {
+            Zoombox.MinScale = 32 / ((FrameworkElement)Item).Width;
+            Zoombox.KeepContentInBounds = !(Zoombox.Scale > Zoombox.MinScale * 35.0);
+        }
+
+        private void Zoombox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
+        {
             if (ZoomPercentage == 100)
             if (ZoomPercentage == 100)
             {
             {
                 SetClickValues();
                 SetClickValues();
             }
             }
-        }
-
-        private void SetClickValues()
-        {
+        }
+
+        private void SetClickValues()
+        {
             if (!IsUsingZoomTool)
             if (!IsUsingZoomTool)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            ClickScale = Zoombox.Scale;
-            SetZoomOrigin();
-        }
-
-        private void SetZoomOrigin()
-        {
-            FrameworkElement item = (FrameworkElement)Item;
+            ClickScale = Zoombox.Scale;
+            SetZoomOrigin();
+        }
+
+        private void SetZoomOrigin()
+        {
+            FrameworkElement item = (FrameworkElement)Item;
             if (item == null)
             if (item == null)
             {
             {
                 return;
                 return;
             }
             }
 
 
-            Point mousePos = Mouse.GetPosition(item);
-            Zoombox.ZoomOrigin = new Point(Math.Clamp(mousePos.X / item.Width, 0, 1), Math.Clamp(mousePos.Y / item.Height, 0, 1));
-        }
-
-        private static void OnCenterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            MainDrawingPanel panel = (MainDrawingPanel)d;
-            panel.Zoombox.CenterContent();
-        }
-
-        private void Zoombox_Loaded(object sender, RoutedEventArgs e)
-        {
+            Point mousePos = Mouse.GetPosition(item);
+            Zoombox.ZoomOrigin = new Point(Math.Clamp(mousePos.X / item.Width, 0, 1), Math.Clamp(mousePos.Y / item.Height, 0, 1));
+        }
+
+        private void Zoombox_Loaded(object sender, RoutedEventArgs e)
+        {
             if (CenterOnStart)
             if (CenterOnStart)
             {
             {
                 ((Zoombox)sender).CenterContent();
                 ((Zoombox)sender).CenterContent();
             }
             }
 
 
-            ClickScale = Zoombox.Scale;
-        }
-
-        private void Zoombox_MouseWheel(object sender, MouseWheelEventArgs e)
-        {
-            SetZoomOrigin();
-        }
-
-        private void mainDrawingPanel_MouseDown(object sender, MouseButtonEventArgs e)
-        {
-            IsUsingZoomTool = ViewModelMain.Current.BitmapManager.SelectedTool is ZoomTool;
-            Mouse.Capture((IInputElement)sender, CaptureMode.SubTree);
-            SetClickValues();
-        }
-
-        private void mainDrawingPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
-        {
-            ((IInputElement)sender).ReleaseMouseCapture();
-        }
-    }
+            ClickScale = Zoombox.Scale;
+        }
+
+        private void Zoombox_MouseWheel(object sender, MouseWheelEventArgs e)
+        {
+            SetZoomOrigin();
+        }
+
+        private void MainDrawingPanel_MouseDown(object sender, MouseButtonEventArgs e)
+        {
+            IsUsingZoomTool = ViewModelMain.Current.BitmapManager.SelectedTool is ZoomTool;
+            Mouse.Capture((IInputElement)sender, CaptureMode.SubTree);
+            SetClickValues();
+        }
+
+        private void MainDrawingPanel_PreviewMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            ((IInputElement)sender).ReleaseMouseCapture();
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/MainWindow.xaml

@@ -12,7 +12,7 @@
         xmlns:cmd="http://www.galasoft.ch/mvvmlight"
         xmlns:cmd="http://www.galasoft.ch/mvvmlight"
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:avalondock="https://github.com/Dirkster99/AvalonDock"
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
         xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker"
-        mc:Ignorable="d" WindowStyle="None" Initialized="mainWindow_Initialized"
+        mc:Ignorable="d" WindowStyle="None" Initialized="MainWindow_Initialized"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         Title="PixiEditor" Name="mainWindow" Height="1000" Width="1600" Background="{StaticResource MainColor}"
         WindowStartupLocation="CenterScreen" WindowState="Maximized" DataContext="{DynamicResource ViewModelMain}">
         WindowStartupLocation="CenterScreen" WindowState="Maximized" DataContext="{DynamicResource ViewModelMain}">
     <WindowChrome.WindowChrome>
     <WindowChrome.WindowChrome>

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

@@ -8,7 +8,7 @@ using PixiEditor.ViewModels;
 namespace PixiEditor
 namespace PixiEditor
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for MainWindow.xaml
+    ///     Interaction logic for MainWindow.xaml.
     /// </summary>
     /// </summary>
     public partial class MainWindow : Window
     public partial class MainWindow : Window
     {
     {
@@ -62,7 +62,7 @@ namespace PixiEditor
             }
             }
         }
         }
 
 
-        private void mainWindow_Initialized(object sender, EventArgs e)
+        private void MainWindow_Initialized(object sender, EventArgs e)
         {
         {
             string dir = AppDomain.CurrentDomain.BaseDirectory;
             string dir = AppDomain.CurrentDomain.BaseDirectory;
             bool updateFileExists = Directory.GetFiles(dir, "update-*.zip").Length > 0;
             bool updateFileExists = Directory.GetFiles(dir, "update-*.zip").Length > 0;

+ 42 - 43
PixiEditor/Views/MenuButton.xaml.cs

@@ -1,45 +1,44 @@
-using System.Windows;
-using System.Windows.Controls;
-using PixiEditor.ViewModels;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for MenuButton.xaml
-    /// </summary>
-    public partial class MenuButton : UserControl
-    {
-        public static readonly DependencyProperty MenuButtonTextProperty =
-            DependencyProperty.Register("Text", typeof(string), typeof(MenuButton),
-                new UIPropertyMetadata(string.Empty));
-
-        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ItemProperty =
-            DependencyProperty.Register("Item", typeof(object), typeof(MenuButton), new PropertyMetadata(null));
-
-        private readonly MenuButtonViewModel dc = new MenuButtonViewModel();
-
-        public MenuButton()
-        {
-            InitializeComponent();
-            DataContext = dc;
-        }
-
-        public string Text
-        {
-            get => (string)GetValue(MenuButtonTextProperty);
-            set => SetValue(MenuButtonTextProperty, value);
-        }
+using System.Windows;
+using System.Windows.Controls;
+using PixiEditor.ViewModels;
 
 
-        public object Item
-        {
-            get => GetValue(ItemProperty);
-            set => SetValue(ItemProperty, value);
-        }
-
-        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
-        {
-            dc.CloseListViewCommand.Execute(null);
-        }
-    }
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for MenuButton.xaml.
+    /// </summary>
+    public partial class MenuButton : UserControl
+    {
+        public static readonly DependencyProperty MenuButtonTextProperty =
+            DependencyProperty.Register("Text", typeof(string), typeof(MenuButton), new UIPropertyMetadata(string.Empty));
+
+        // Using a DependencyProperty as the backing store for Item.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ItemProperty =
+            DependencyProperty.Register("Item", typeof(object), typeof(MenuButton), new PropertyMetadata(null));
+
+        private readonly MenuButtonViewModel dc = new MenuButtonViewModel();
+
+        public MenuButton()
+        {
+            InitializeComponent();
+            DataContext = dc;
+        }
+
+        public string Text
+        {
+            get => (string)GetValue(MenuButtonTextProperty);
+            set => SetValue(MenuButtonTextProperty, value);
+        }
+
+        public object Item
+        {
+            get => GetValue(ItemProperty);
+            set => SetValue(ItemProperty, value);
+        }
+
+        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
+        {
+            dc.CloseListViewCommand.Execute(null);
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/NewFilePopup.xaml.cs

@@ -3,7 +3,7 @@
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for NewFilePopup.xaml
+    ///     Interaction logic for NewFilePopup.xaml.
     /// </summary>
     /// </summary>
     public partial class NewFilePopup : Window
     public partial class NewFilePopup : Window
     {
     {

+ 70 - 61
PixiEditor/Views/NumberInput.xaml.cs

@@ -1,64 +1,73 @@
-using System;
-using System.Text.RegularExpressions;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for NumerInput.xaml
-    /// </summary>
-    public partial class NumberInput : UserControl
-    {
-        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty ValueProperty =
-            DependencyProperty.Register("Value", typeof(float), typeof(NumberInput),
-                new PropertyMetadata(0f, OnValueChanged));
-
-        // Using a DependencyProperty as the backing store for Min.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MinProperty =
-            DependencyProperty.Register("Min", typeof(float), typeof(NumberInput),
-                new PropertyMetadata(float.NegativeInfinity));
-
-        // Using a DependencyProperty as the backing store for Max.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty MaxProperty =
-            DependencyProperty.Register("Max", typeof(float), typeof(NumberInput),
-                new PropertyMetadata(float.PositiveInfinity));
+using System;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
 
 
-        public NumberInput()
-        {
-            InitializeComponent();
-        }
-
-        public float Value
-        {
-            get => (float)GetValue(ValueProperty);
-            set => SetValue(ValueProperty, value);
-        }
-
-        public float Min
-        {
-            get => (float)GetValue(MinProperty);
-            set => SetValue(MinProperty, value);
-        }
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for NumerInput.xaml.
+    /// </summary>
+    public partial class NumberInput : UserControl
+    {
+        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty ValueProperty =
+            DependencyProperty.Register(
+                "Value",
+                typeof(float),
+                typeof(NumberInput),
+                new PropertyMetadata(0f, OnValueChanged));
 
 
-        public float Max
-        {
-            get => (float)GetValue(MaxProperty);
-            set => SetValue(MaxProperty, value);
-        }
-
-        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            NumberInput input = (NumberInput)d;
-            input.Value = Math.Clamp((float)e.NewValue, input.Min, input.Max);
-        }
-
-        private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
-        {
-            Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
-            e.Handled = !regex.IsMatch((sender as TextBox).Text.Insert((sender as TextBox).SelectionStart, e.Text));
-        }
-    }
+        // Using a DependencyProperty as the backing store for Min.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MinProperty =
+            DependencyProperty.Register(
+                "Min",
+                typeof(float),
+                typeof(NumberInput),
+                new PropertyMetadata(float.NegativeInfinity));
+
+        // Using a DependencyProperty as the backing store for Max.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty MaxProperty =
+            DependencyProperty.Register(
+                "Max",
+                typeof(float),
+                typeof(NumberInput),
+                new PropertyMetadata(float.PositiveInfinity));
+
+        public NumberInput()
+        {
+            InitializeComponent();
+        }
+
+        public float Value
+        {
+            get => (float)GetValue(ValueProperty);
+            set => SetValue(ValueProperty, value);
+        }
+
+        public float Min
+        {
+            get => (float)GetValue(MinProperty);
+            set => SetValue(MinProperty, value);
+        }
+
+        public float Max
+        {
+            get => (float)GetValue(MaxProperty);
+            set => SetValue(MaxProperty, value);
+        }
+
+        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            NumberInput input = (NumberInput)d;
+            input.Value = Math.Clamp((float)e.NewValue, input.Min, input.Max);
+        }
+
+        private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
+        {
+            Regex regex = new Regex("^[.][0-9]+$|^[0-9]*[.]{0,1}[0-9]*$");
+            e.Handled = !regex.IsMatch((sender as TextBox).Text.Insert((sender as TextBox).SelectionStart, e.Text));
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/PopupTemplate.xaml.cs

@@ -4,7 +4,7 @@ using System.Windows.Input;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for PopupTemplate.xaml
+    ///     Interaction logic for PopupTemplate.xaml.
     /// </summary>
     /// </summary>
     public partial class PopupTemplate : Window
     public partial class PopupTemplate : Window
     {
     {

+ 62 - 59
PixiEditor/Views/ResizeCanvasPopup.xaml.cs

@@ -1,64 +1,67 @@
-using System.Windows;
-using System.Windows.Input;
-using PixiEditor.Models.Enums;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for ResizeCanvasPopup.xaml
-    /// </summary>
-    public partial class ResizeCanvasPopup : Window
-    {
-        // Using a DependencyProperty as the backing store for SelectedAnchorPoint.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty SelectedAnchorPointProperty =
-            DependencyProperty.Register("SelectedAnchorPoint", typeof(AnchorPoint), typeof(ResizeCanvasPopup),
-                new PropertyMetadata(AnchorPoint.Top | AnchorPoint.Left));
-
-        // Using a DependencyProperty as the backing store for NewHeight.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty NewHeightProperty =
-            DependencyProperty.Register("NewHeight", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
-
-        // Using a DependencyProperty as the backing store for NewWidth.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty NewWidthProperty =
-            DependencyProperty.Register("NewWidth", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
-
-        public ResizeCanvasPopup()
-        {
-            InitializeComponent();
-        }
+using System.Windows;
+using System.Windows.Input;
+using PixiEditor.Models.Enums;
 
 
-        public AnchorPoint SelectedAnchorPoint
-        {
-            get => (AnchorPoint)GetValue(SelectedAnchorPointProperty);
-            set => SetValue(SelectedAnchorPointProperty, value);
-        }
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for ResizeCanvasPopup.xaml.
+    /// </summary>
+    public partial class ResizeCanvasPopup : Window
+    {
+        // Using a DependencyProperty as the backing store for SelectedAnchorPoint.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty SelectedAnchorPointProperty =
+            DependencyProperty.Register(
+                "SelectedAnchorPoint",
+                typeof(AnchorPoint),
+                typeof(ResizeCanvasPopup),
+                new PropertyMetadata(AnchorPoint.Top | AnchorPoint.Left));
 
 
-        public int NewHeight
-        {
-            get => (int)GetValue(NewHeightProperty);
-            set => SetValue(NewHeightProperty, value);
-        }
+        // Using a DependencyProperty as the backing store for NewHeight.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty NewHeightProperty =
+            DependencyProperty.Register("NewHeight", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
 
 
-        public int NewWidth
-        {
-            get => (int)GetValue(NewWidthProperty);
-            set => SetValue(NewWidthProperty, value);
-        }
+        // Using a DependencyProperty as the backing store for NewWidth.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty NewWidthProperty =
+            DependencyProperty.Register("NewWidth", typeof(int), typeof(ResizeCanvasPopup), new PropertyMetadata(0));
 
 
-        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
-        {
-            e.CanExecute = true;
-        }
-
-        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
-        {
-            SystemCommands.CloseWindow(this);
-        }
-
-        private void Button_Click(object sender, RoutedEventArgs e)
-        {
-            DialogResult = true;
-            Close();
-        }
-    }
+        public ResizeCanvasPopup()
+        {
+            InitializeComponent();
+        }
+
+        public AnchorPoint SelectedAnchorPoint
+        {
+            get => (AnchorPoint)GetValue(SelectedAnchorPointProperty);
+            set => SetValue(SelectedAnchorPointProperty, value);
+        }
+
+        public int NewHeight
+        {
+            get => (int)GetValue(NewHeightProperty);
+            set => SetValue(NewHeightProperty, value);
+        }
+
+        public int NewWidth
+        {
+            get => (int)GetValue(NewWidthProperty);
+            set => SetValue(NewWidthProperty, value);
+        }
+
+        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+        {
+            e.CanExecute = true;
+        }
+
+        private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+        {
+            SystemCommands.CloseWindow(this);
+        }
+
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+            DialogResult = true;
+            Close();
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/ResizeDocumentPopup.xaml.cs

@@ -4,7 +4,7 @@ using System.Windows.Input;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for ResizeDocumentPopup.xaml
+    ///     Interaction logic for ResizeDocumentPopup.xaml.
     /// </summary>
     /// </summary>
     public partial class ResizeDocumentPopup : Window
     public partial class ResizeDocumentPopup : Window
     {
     {

+ 66 - 64
PixiEditor/Views/Rotatebox.xaml.cs

@@ -1,70 +1,72 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for Rotatebox.xaml
-    /// </summary>
-    public partial class Rotatebox : UserControl
-    {
-        // Using a DependencyProperty backing store for Angle.
-        public static readonly DependencyProperty AngleProperty =
-            DependencyProperty.Register("Angle", typeof(double), typeof(Rotatebox), new UIPropertyMetadata(0.0));
-
-        private double height, width;
-        private readonly float offset = 90;
-
-        public Rotatebox()
-        {
-            InitializeComponent();
-            MouseLeftButtonDown += OnMouseLeftButtonDown;
-            MouseUp += OnMouseUp;
-            MouseMove += OnMouseMove;
-        }
-
-        public double Angle
-        {
-            get => (double)GetValue(AngleProperty);
-            set => SetValue(AngleProperty, value);
-        }
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
 
 
-        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
-        {
-            Mouse.Capture(this);
-            width = ActualWidth;
-            height = ActualHeight;
-        }
-
-        private void OnMouseUp(object sender, MouseButtonEventArgs e)
-        {
-            Mouse.Capture(null);
-        }
-
-        private void OnMouseMove(object sender, MouseEventArgs e)
-        {
-            if (Equals(Mouse.Captured, this))
-            {
-                // Get the current mouse position relative to the control
-                Point currentLocation = Mouse.GetPosition(this);
-
-                // We want to rotate around the center of the knob, not the top corner
-                Point knobCenter = new Point(width / 2, height / 2);
-
-                // Calculate an angle
-                double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
-                                        (currentLocation.X - knobCenter.X));
-                Angle = radians * 180 / Math.PI + offset;
-
-                // Apply a 180 degree shift when X is negative so that we can rotate
-                // all of the way around
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for Rotatebox.xaml.
+    /// </summary>
+    public partial class Rotatebox : UserControl
+    {
+        // Using a DependencyProperty backing store for Angle.
+        public static readonly DependencyProperty AngleProperty =
+            DependencyProperty.Register("Angle", typeof(double), typeof(Rotatebox), new UIPropertyMetadata(0.0));
+
+        private readonly float offset = 90;
+
+        private double height;
+        private double width;
+
+        public Rotatebox()
+        {
+            InitializeComponent();
+            MouseLeftButtonDown += OnMouseLeftButtonDown;
+            MouseUp += OnMouseUp;
+            MouseMove += OnMouseMove;
+        }
+
+        public double Angle
+        {
+            get => (double)GetValue(AngleProperty);
+            set => SetValue(AngleProperty, value);
+        }
+
+        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            Mouse.Capture(this);
+            width = ActualWidth;
+            height = ActualHeight;
+        }
+
+        private void OnMouseUp(object sender, MouseButtonEventArgs e)
+        {
+            Mouse.Capture(null);
+        }
+
+        private void OnMouseMove(object sender, MouseEventArgs e)
+        {
+            if (Equals(Mouse.Captured, this))
+            {
+                // Get the current mouse position relative to the control
+                Point currentLocation = Mouse.GetPosition(this);
+
+                // We want to rotate around the center of the knob, not the top corner
+                Point knobCenter = new Point(width / 2, height / 2);
+
+                // Calculate an angle
+                double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
+                                        (currentLocation.X - knobCenter.X));
+                Angle = (radians * 180 / Math.PI) + offset;
+
+                // Apply a 180 degree shift when X is negative so that we can rotate
+                // all of the way around
                 if (currentLocation.X - knobCenter.X < 0)
                 if (currentLocation.X - knobCenter.X < 0)
                 {
                 {
                     Angle += 180;
                     Angle += 180;
                 }
                 }
-            }
-        }
-    }
+            }
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/SaveFilePopup.xaml.cs

@@ -4,7 +4,7 @@ using PixiEditor.ViewModels;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for SaveFilePopup.xaml
+    ///     Interaction logic for SaveFilePopup.xaml.
     /// </summary>
     /// </summary>
     public partial class SaveFilePopup : Window
     public partial class SaveFilePopup : Window
     {
     {

+ 1 - 1
PixiEditor/Views/SizeInput.xaml

@@ -8,7 +8,7 @@
              xmlns:converters="clr-namespace:PixiEditor.Helpers"
              xmlns:converters="clr-namespace:PixiEditor.Helpers"
              xmlns:validators="clr-namespace:PixiEditor.Helpers.Validators"
              xmlns:validators="clr-namespace:PixiEditor.Helpers.Validators"
              mc:Ignorable="d"
              mc:Ignorable="d"
-             d:DesignHeight="30" d:DesignWidth="160" Name="uc" LayoutUpdated="uc_LayoutUpdated">
+             d:DesignHeight="30" d:DesignWidth="160" Name="uc" LayoutUpdated="Uc_LayoutUpdated">
     <UserControl.Resources>
     <UserControl.Resources>
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
         <converters:ToolSizeToIntConverter x:Key="ToolSizeToIntConverter" />
     </UserControl.Resources>
     </UserControl.Resources>

+ 69 - 71
PixiEditor/Views/SizeInput.xaml.cs

@@ -1,74 +1,72 @@
-using System;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace PixiEditor.Views
-{
-    /// <summary>
-    ///     Interaction logic for SizeInput.xaml
-    /// </summary>
-    public partial class SizeInput : UserControl
-    {
-        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty SizeProperty =
-            DependencyProperty.Register("Size", typeof(int), typeof(SizeInput), new PropertyMetadata(1));
-
-        // Using a DependencyProperty as the backing store for PreserveAspectRatio.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty PreserveAspectRatioProperty =
-            DependencyProperty.Register("PreserveAspectRatio", typeof(bool), typeof(SizeInput),
-                new PropertyMetadata(false));
-
-        // Using a DependencyProperty as the backing store for AspectRatioValue.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty AspectRatioValueProperty =
-            DependencyProperty.Register("AspectRatioValue", typeof(int), typeof(SizeInput),
-                new PropertyMetadata(1, AspectRatioValChanged));
-
-        private int loadedAspectRatioSize = -1;
-
-        private int loadedSize = -1;
-
-        public SizeInput()
-        {
-            InitializeComponent();
-        }
+using System;
+using System.Windows;
+using System.Windows.Controls;
 
 
-        public int Size
-        {
-            get => (int)GetValue(SizeProperty);
-            set => SetValue(SizeProperty, value);
-        }
-
-        public bool PreserveAspectRatio
-        {
-            get => (bool)GetValue(PreserveAspectRatioProperty);
-            set => SetValue(PreserveAspectRatioProperty, value);
-        }
+namespace PixiEditor.Views
+{
+    /// <summary>
+    ///     Interaction logic for SizeInput.xaml.
+    /// </summary>
+    public partial class SizeInput : UserControl
+    {
+        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty SizeProperty =
+            DependencyProperty.Register("Size", typeof(int), typeof(SizeInput), new PropertyMetadata(1));
 
 
-        public int AspectRatioValue
-        {
-            get => (int)GetValue(AspectRatioValueProperty);
-            set => SetValue(AspectRatioValueProperty, value);
-        }
-
-        private static void AspectRatioValChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            SizeInput input = (SizeInput)d;
-
-            if (input.PreserveAspectRatio && input.loadedSize != -1)
-            {
-                int newVal = (int)e.NewValue;
-                float ratio = newVal / Math.Clamp(input.loadedAspectRatioSize, 1f, float.MaxValue);
-                input.Size = (int)(input.loadedSize * ratio);
-            }
-        }
-
-        private void uc_LayoutUpdated(object sender, EventArgs e)
-        {
-            if (loadedSize == -1)
-            {
-                loadedSize = Size;
-                loadedAspectRatioSize = AspectRatioValue;
-            }
-        }
-    }
+        // Using a DependencyProperty as the backing store for PreserveAspectRatio.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty PreserveAspectRatioProperty =
+            DependencyProperty.Register("PreserveAspectRatio", typeof(bool), typeof(SizeInput), new PropertyMetadata(false));
+
+        // Using a DependencyProperty as the backing store for AspectRatioValue.  This enables animation, styling, binding, etc...
+        public static readonly DependencyProperty AspectRatioValueProperty =
+            DependencyProperty.Register("AspectRatioValue", typeof(int), typeof(SizeInput), new PropertyMetadata(1, AspectRatioValChanged));
+
+        private int loadedAspectRatioSize = -1;
+
+        private int loadedSize = -1;
+
+        public SizeInput()
+        {
+            InitializeComponent();
+        }
+
+        public int Size
+        {
+            get => (int)GetValue(SizeProperty);
+            set => SetValue(SizeProperty, value);
+        }
+
+        public bool PreserveAspectRatio
+        {
+            get => (bool)GetValue(PreserveAspectRatioProperty);
+            set => SetValue(PreserveAspectRatioProperty, value);
+        }
+
+        public int AspectRatioValue
+        {
+            get => (int)GetValue(AspectRatioValueProperty);
+            set => SetValue(AspectRatioValueProperty, value);
+        }
+
+        private static void AspectRatioValChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            SizeInput input = (SizeInput)d;
+
+            if (input.PreserveAspectRatio && input.loadedSize != -1)
+            {
+                int newVal = (int)e.NewValue;
+                float ratio = newVal / Math.Clamp(input.loadedAspectRatioSize, 1f, float.MaxValue);
+                input.Size = (int)(input.loadedSize * ratio);
+            }
+        }
+
+        private void Uc_LayoutUpdated(object sender, EventArgs e)
+        {
+            if (loadedSize == -1)
+            {
+                loadedSize = Size;
+                loadedAspectRatioSize = AspectRatioValue;
+            }
+        }
+    }
 }
 }

+ 1 - 1
PixiEditor/Views/SizePicker.xaml.cs

@@ -4,7 +4,7 @@ using System.Windows.Controls;
 namespace PixiEditor.Views
 namespace PixiEditor.Views
 {
 {
     /// <summary>
     /// <summary>
-    ///     Interaction logic for SizePicker.xaml
+    ///     Interaction logic for SizePicker.xaml.
     /// </summary>
     /// </summary>
     public partial class SizePicker : UserControl
     public partial class SizePicker : UserControl
     {
     {

+ 0 - 11
PixiEditorTests/ModelsTests/ControllersTests/BitmapManagerTests.cs

@@ -89,15 +89,4 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             Assert.Equal(Colors.Green, bitmapManager.ActiveLayer.GetPixelWithOffset(1, 1));
             Assert.Equal(Colors.Green, bitmapManager.ActiveLayer.GetPixelWithOffset(1, 1));
         }
         }
     }
     }
-
-    public class MockedSinglePixelPen : BitmapOperationTool
-    {
-        public override ToolType ToolType { get; } = ToolType.Pen;
-
-        public override LayerChange[] Use(Layer layer, Coordinates[] mouseMove, Color color)
-        {
-            return Only(
-                BitmapPixelChanges.FromSingleColoredArray(new[] { mouseMove[0] }, color), 0);
-        }
-    }
 }
 }

+ 8 - 4
PixiEditorTests/ModelsTests/ControllersTests/ClipboardControllerTests.cs

@@ -10,14 +10,15 @@ using Xunit;
 namespace PixiEditorTests.ModelsTests.ControllersTests
 namespace PixiEditorTests.ModelsTests.ControllersTests
 {
 {
     public class ClipboardControllerTests
     public class ClipboardControllerTests
-    {
+    {
+        private const string Text = "Text data";
         private readonly Color testColor = Colors.Coral;
         private readonly Color testColor = Colors.Coral;
 
 
         [StaFact]
         [StaFact]
         public void TestThatClipboardControllerIgnoresNonImageDataInClipboard()
         public void TestThatClipboardControllerIgnoresNonImageDataInClipboard()
         {
         {
             Clipboard.Clear();
             Clipboard.Clear();
-            Clipboard.SetText("Text data");
+            Clipboard.SetText(Text);
             WriteableBitmap img = ClipboardController.GetImageFromClipboard();
             WriteableBitmap img = ClipboardController.GetImageFromClipboard();
             Assert.Null(img);
             Assert.Null(img);
         }
         }
@@ -48,8 +49,11 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             testLayer.SetPixel(new Coordinates(4, 4), testColor);
             testLayer.SetPixel(new Coordinates(4, 4), testColor);
             testLayer2.SetPixel(new Coordinates(5, 5), testColor);
             testLayer2.SetPixel(new Coordinates(5, 5), testColor);
 
 
-            ClipboardController.CopyToClipboard(new[] { testLayer, testLayer2 },
-                new[] { new Coordinates(4, 4), new Coordinates(5, 5) }, 10, 10);
+            ClipboardController.CopyToClipboard(
+                new[] { testLayer, testLayer2 },
+                new[] { new Coordinates(4, 4), new Coordinates(5, 5) },
+                10,
+                10);
 
 
             BitmapSource img = Clipboard.GetImage(); // Using default Clipboard get image to avoid false positives from faulty ClipboardController GetImage
             BitmapSource img = Clipboard.GetImage(); // Using default Clipboard get image to avoid false positives from faulty ClipboardController GetImage
 
 

+ 21 - 0
PixiEditorTests/ModelsTests/ControllersTests/MockedSinglePixelPen.cs

@@ -0,0 +1,21 @@
+using System.Windows.Media;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using Xunit;
+
+namespace PixiEditorTests.ModelsTests.ControllersTests
+{
+    public class MockedSinglePixelPen : BitmapOperationTool
+    {
+        public override ToolType ToolType { get; } = ToolType.Pen;
+
+        public override LayerChange[] Use(Layer layer, Coordinates[] mouseMove, Color color)
+        {
+            return Only(
+                BitmapPixelChanges.FromSingleColoredArray(new[] { mouseMove[0] }, color), 0);
+        }
+    }
+}

+ 1 - 1
PixiEditorTests/ModelsTests/ControllersTests/MouseMovementControllerTests.cs

@@ -37,7 +37,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         {
         {
             bool eventRaised = false;
             bool eventRaised = false;
             Coordinates position = new Coordinates(5, 5);
             Coordinates position = new Coordinates(5, 5);
-            MouseMovementEventArgs args = new MouseMovementEventArgs(new Coordinates());
+            MouseMovementEventArgs args = new MouseMovementEventArgs(default(Coordinates));
 
 
             MouseMovementController controller = new MouseMovementController();
             MouseMovementController controller = new MouseMovementController();
             controller.MousePositionChanged += (s, e) =>
             controller.MousePositionChanged += (s, e) =>

+ 6 - 3
PixiEditorTests/ModelsTests/ControllersTests/PixelChangesControllerTests.cs

@@ -24,7 +24,8 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             PixelChangesController controller = CreateBasicController();
             PixelChangesController controller = CreateBasicController();
             Coordinates[] cords = { new Coordinates(5, 3), new Coordinates(7, 2) };
             Coordinates[] cords = { new Coordinates(5, 3), new Coordinates(7, 2) };
 
 
-            controller.AddChanges(new LayerChange(
+            controller.AddChanges(
+                new LayerChange(
                     BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Black), 1),
                     BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Black), 1),
                 new LayerChange(BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Transparent), 1));
                 new LayerChange(BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Transparent), 1));
 
 
@@ -38,7 +39,8 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             Coordinates[] cords2 = { new Coordinates(2, 2), new Coordinates(5, 5) };
             Coordinates[] cords2 = { new Coordinates(2, 2), new Coordinates(5, 5) };
             PixelChangesController controller = CreateBasicController();
             PixelChangesController controller = CreateBasicController();
 
 
-            controller.AddChanges(new LayerChange(
+            controller.AddChanges(
+                new LayerChange(
                     BitmapPixelChanges.FromSingleColoredArray(cords2, Colors.Black), 0),
                     BitmapPixelChanges.FromSingleColoredArray(cords2, Colors.Black), 0),
                 new LayerChange(BitmapPixelChanges.FromSingleColoredArray(cords2, Colors.Transparent), 0));
                 new LayerChange(BitmapPixelChanges.FromSingleColoredArray(cords2, Colors.Transparent), 0));
 
 
@@ -53,7 +55,8 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             Coordinates[] cords = { new Coordinates(0, 0), new Coordinates(1, 1) };
             Coordinates[] cords = { new Coordinates(0, 0), new Coordinates(1, 1) };
             PixelChangesController controller = new PixelChangesController();
             PixelChangesController controller = new PixelChangesController();
 
 
-            controller.AddChanges(new LayerChange(
+            controller.AddChanges(
+                new LayerChange(
                     BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Black), 0),
                     BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Black), 0),
                 new LayerChange(BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Transparent), 0));
                 new LayerChange(BitmapPixelChanges.FromSingleColoredArray(cords, Colors.Transparent), 0));
             return controller;
             return controller;

+ 1 - 20
PixiEditorTests/ModelsTests/ControllersTests/ReadonlyUtilityTests.cs

@@ -1,7 +1,5 @@
-using System;
-using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools;
 using Xunit;
 using Xunit;
 
 
 namespace PixiEditorTests.ModelsTests.ControllersTests
 namespace PixiEditorTests.ModelsTests.ControllersTests
@@ -18,21 +16,4 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             Assert.True(toolUsed);
             Assert.True(toolUsed);
         }
         }
     }
     }
-
-    public class TestReadonlyTool : ReadonlyTool
-    {
-        public TestReadonlyTool(Action toolAction)
-        {
-            ToolAction = toolAction;
-        }
-
-        public Action ToolAction { get; set; }
-
-        public override ToolType ToolType => ToolType.Select;
-
-        public override void Use(Coordinates[] pixels)
-        {
-            ToolAction();
-        }
-    }
 }
 }

+ 80 - 81
PixiEditorTests/ModelsTests/ControllersTests/ShortcutControllerTests.cs

@@ -1,83 +1,82 @@
-using System.Windows.Input;
-using PixiEditor.Helpers;
-using PixiEditor.Models.Controllers.Shortcuts;
-using Xunit;
-
-namespace PixiEditorTests.ModelsTests.ControllersTests
-{
-    public class ShortcutControllerTests
-    {
-        private static ShortcutController GenerateStandardShortcutController(Key shortcutKey, ModifierKeys modifiers, RelayCommand shortcutCommand)
-        {
-            ShortcutController controller = new ShortcutController();
-            controller.Shortcuts.Add(new Shortcut(shortcutKey, shortcutCommand, 0, modifiers));
-            ShortcutController.BlockShortcutExecution = false;
-            return controller;
-        }
-
-        [StaTheory]
-        [InlineData(Key.A, ModifierKeys.None, Key.A, ModifierKeys.None)]
-        [InlineData(Key.A, ModifierKeys.Alt, Key.A, ModifierKeys.Alt)]
-        [InlineData(Key.B, ModifierKeys.Alt | ModifierKeys.Control, Key.B, ModifierKeys.Alt | ModifierKeys.Control)]
-        public void TestThatShortcutControllerExecutesShortcut(Key shortcutKey, ModifierKeys shortcutModifiers, Key clickedKey, ModifierKeys clickedModifiers)
-        {
-            int result = -1;
-            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
-            ShortcutController controller = GenerateStandardShortcutController(shortcutKey, shortcutModifiers, shortcutCommand);
-
-            controller.KeyPressed(clickedKey, clickedModifiers);
-            Assert.Equal(0, result);
-        }
-
-        [StaTheory]
-        [InlineData(Key.B, ModifierKeys.None, Key.A, ModifierKeys.None)]
-        [InlineData(Key.A, ModifierKeys.Alt, Key.A, ModifierKeys.None)]
-        [InlineData(Key.C, ModifierKeys.Alt | ModifierKeys.Control, Key.C, ModifierKeys.Alt | ModifierKeys.Windows)]
-        public void TestThatShortcutControllerNotExecutesShortcut(Key shortcutKey, ModifierKeys shortcutModifiers, Key clickedKey, ModifierKeys clickedModifiers)
-        {
-            int result = -1;
-            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
-            ShortcutController controller = GenerateStandardShortcutController(shortcutKey, shortcutModifiers, shortcutCommand);
+using System.Windows.Input;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers.Shortcuts;
+using Xunit;
 
 
-            controller.KeyPressed(clickedKey, clickedModifiers);
-            Assert.Equal(-1, result);
-        }
-
-        [StaFact]
-        public void TestThatShortcutControllerIsBlocked()
-        {
-            int result = -1;
-            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
-
-            ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, shortcutCommand);
-            ShortcutController.BlockShortcutExecution = true;
-
-            controller.KeyPressed(Key.A, ModifierKeys.None);
-            Assert.Equal(-1, result);
-        }
-
-        [StaFact]
-        public void TestThatShortcutControllerPicksCorrectShortcut()
-        {
-            int result = -1;
-            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
-
-            ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, shortcutCommand);
-            controller.Shortcuts.Add(new Shortcut(Key.A, shortcutCommand, 1, ModifierKeys.Control));
-
-            controller.KeyPressed(Key.A, ModifierKeys.Control);
-            Assert.Equal(1, result);
-        }
-
-        [StaFact]
-        public void TestThatKeyPressedSetsLastShortcut()
-        {
-            ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None,
-                new RelayCommand(parameter => { }));
-
-            Assert.Null(controller.LastShortcut);
-            controller.KeyPressed(Key.A, ModifierKeys.None);
-            Assert.Equal(controller.Shortcuts[0], controller.LastShortcut);
-        }
-    }
+namespace PixiEditorTests.ModelsTests.ControllersTests
+{
+    public class ShortcutControllerTests
+    {
+        [StaTheory]
+        [InlineData(Key.A, ModifierKeys.None, Key.A, ModifierKeys.None)]
+        [InlineData(Key.A, ModifierKeys.Alt, Key.A, ModifierKeys.Alt)]
+        [InlineData(Key.B, ModifierKeys.Alt | ModifierKeys.Control, Key.B, ModifierKeys.Alt | ModifierKeys.Control)]
+        public void TestThatShortcutControllerExecutesShortcut(Key shortcutKey, ModifierKeys shortcutModifiers, Key clickedKey, ModifierKeys clickedModifiers)
+        {
+            int result = -1;
+            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
+            ShortcutController controller = GenerateStandardShortcutController(shortcutKey, shortcutModifiers, shortcutCommand);
+
+            controller.KeyPressed(clickedKey, clickedModifiers);
+            Assert.Equal(0, result);
+        }
+
+        [StaTheory]
+        [InlineData(Key.B, ModifierKeys.None, Key.A, ModifierKeys.None)]
+        [InlineData(Key.A, ModifierKeys.Alt, Key.A, ModifierKeys.None)]
+        [InlineData(Key.C, ModifierKeys.Alt | ModifierKeys.Control, Key.C, ModifierKeys.Alt | ModifierKeys.Windows)]
+        public void TestThatShortcutControllerNotExecutesShortcut(Key shortcutKey, ModifierKeys shortcutModifiers, Key clickedKey, ModifierKeys clickedModifiers)
+        {
+            int result = -1;
+            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
+            ShortcutController controller = GenerateStandardShortcutController(shortcutKey, shortcutModifiers, shortcutCommand);
+
+            controller.KeyPressed(clickedKey, clickedModifiers);
+            Assert.Equal(-1, result);
+        }
+
+        [StaFact]
+        public void TestThatShortcutControllerIsBlocked()
+        {
+            int result = -1;
+            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
+
+            ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, shortcutCommand);
+            ShortcutController.BlockShortcutExecution = true;
+
+            controller.KeyPressed(Key.A, ModifierKeys.None);
+            Assert.Equal(-1, result);
+        }
+
+        [StaFact]
+        public void TestThatShortcutControllerPicksCorrectShortcut()
+        {
+            int result = -1;
+            RelayCommand shortcutCommand = new RelayCommand(arg => { result = (int)arg; });
+
+            ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, shortcutCommand);
+            controller.Shortcuts.Add(new Shortcut(Key.A, shortcutCommand, 1, ModifierKeys.Control));
+
+            controller.KeyPressed(Key.A, ModifierKeys.Control);
+            Assert.Equal(1, result);
+        }
+
+        [StaFact]
+        public void TestThatKeyPressedSetsLastShortcut()
+        {
+            ShortcutController controller = GenerateStandardShortcutController(Key.A, ModifierKeys.None, new RelayCommand(parameter => { }));
+
+            Assert.Null(controller.LastShortcut);
+            controller.KeyPressed(Key.A, ModifierKeys.None);
+            Assert.Equal(controller.Shortcuts[0], controller.LastShortcut);
+        }
+
+        private static ShortcutController GenerateStandardShortcutController(Key shortcutKey, ModifierKeys modifiers, RelayCommand shortcutCommand)
+        {
+            ShortcutController controller = new ShortcutController();
+            controller.Shortcuts.Add(new Shortcut(shortcutKey, shortcutCommand, 0, modifiers));
+            ShortcutController.BlockShortcutExecution = false;
+            return controller;
+        }
+    }
 }
 }

+ 7 - 0
PixiEditorTests/ModelsTests/ControllersTests/TestPropertyClass.cs

@@ -0,0 +1,7 @@
+namespace PixiEditorTests.ModelsTests.ControllersTests
+{
+    public class TestPropertyClass
+    {
+        public int IntProperty { get; set; }
+    }
+}

+ 23 - 0
PixiEditorTests/ModelsTests/ControllersTests/TestReadonlyTool.cs

@@ -0,0 +1,23 @@
+using System;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+
+namespace PixiEditorTests.ModelsTests.ControllersTests
+{
+    public class TestReadonlyTool : ReadonlyTool
+    {
+        public TestReadonlyTool(Action toolAction)
+        {
+            ToolAction = toolAction;
+        }
+
+        public Action ToolAction { get; set; }
+
+        public override ToolType ToolType => ToolType.Select;
+
+        public override void Use(Coordinates[] pixels)
+        {
+            ToolAction();
+        }
+    }
+}

+ 7 - 9
PixiEditorTests/ModelsTests/ControllersTests/UndoManagerTests.cs

@@ -100,7 +100,8 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             int newVal = 5;
             int newVal = 5;
 
 
             UndoManager.AddUndoChange(
             UndoManager.AddUndoChange(
-                new Change("ExampleProperty",
+                new Change(
+                    "ExampleProperty",
                     ReverseProcess,
                     ReverseProcess,
                     new object[] { ExampleProperty },
                     new object[] { ExampleProperty },
                     newVal));
                     newVal));
@@ -123,7 +124,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         {
         {
             PrepareUndoManagerForTest();
             PrepareUndoManagerForTest();
             int newVal = 5;
             int newVal = 5;
-            UndoManager.AddUndoChange(new Change(ReverseProcess, new object[] { ExampleProperty }, ReverseProcess,
+            UndoManager.AddUndoChange(new Change(
+                ReverseProcess,
+                new object[] { ExampleProperty },
+                ReverseProcess,
                 new object[] { newVal }));
                 new object[] { newVal }));
 
 
             ExampleProperty = newVal;
             ExampleProperty = newVal;
@@ -145,8 +149,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             PrepareUndoManagerForTest();
             PrepareUndoManagerForTest();
             int newVal = 5;
             int newVal = 5;
 
 
-            UndoManager.AddUndoChange(new Change("TestPropClass.IntProperty", TestPropClass.IntProperty,
-                newVal));
+            UndoManager.AddUndoChange(new Change("TestPropClass.IntProperty", TestPropClass.IntProperty, newVal));
 
 
             TestPropClass.IntProperty = newVal;
             TestPropClass.IntProperty = newVal;
 
 
@@ -175,9 +178,4 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             TestPropClass = new TestPropertyClass { IntProperty = 0 };
             TestPropClass = new TestPropertyClass { IntProperty = 0 };
         }
         }
     }
     }
-
-    public class TestPropertyClass
-    {
-        public int IntProperty { get; set; }
-    }
 }
 }

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

@@ -37,8 +37,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         [Fact]
         public void TestThatFromArraysThrowsError()
         public void TestThatFromArraysThrowsError()
         {
         {
-            Assert.Throws<ArrayLengthMismatchException>
-                (() => BitmapPixelChanges.FromArrays(new[] { new Coordinates(0, 0) }, new[] { Colors.Red, Colors.Green }));
+            Assert.Throws<ArrayLengthMismatchException>(
+                () => BitmapPixelChanges.FromArrays(new[] { new Coordinates(0, 0) }, new[] { Colors.Red, Colors.Green }));
         }
         }
 
 
         [Fact]
         [Fact]

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

@@ -47,7 +47,9 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
                 ActiveDocument = document
                 ActiveDocument = document
             };
             };
             manager.AddNewLayer("test");
             manager.AddNewLayer("test");
-            manager.ActiveLayer.SetPixel(new Coordinates((int)Math.Ceiling(initialWidth / 2f),
+            manager.ActiveLayer.SetPixel(
+                new Coordinates(
+                (int)Math.Ceiling(initialWidth / 2f),
                 (int)Math.Ceiling(initialHeight / 2f)), Colors.Black);
                 (int)Math.Ceiling(initialHeight / 2f)), Colors.Black);
 
 
             manager.ActiveLayer.SetPixel(new Coordinates(additionalPixelX, additionalPixelY), Colors.Black);
             manager.ActiveLayer.SetPixel(new Coordinates(additionalPixelX, additionalPixelY), Colors.Black);
@@ -71,7 +73,9 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
                 ActiveDocument = document
                 ActiveDocument = document
             };
             };
             manager.AddNewLayer("test");
             manager.AddNewLayer("test");
-            manager.ActiveLayer.SetPixel(new Coordinates((int)Math.Ceiling(initialWidth / 2f),
+            manager.ActiveLayer.SetPixel(
+                new Coordinates(
+                (int)Math.Ceiling(initialWidth / 2f),
                 (int)Math.Ceiling(initialHeight / 2f)), Colors.Black); // Set pixel in center
                 (int)Math.Ceiling(initialHeight / 2f)), Colors.Black); // Set pixel in center
 
 
             manager.AddNewLayer("test2");
             manager.AddNewLayer("test2");

+ 2 - 1
PixiEditorTests/ModelsTests/DataHoldersTests/SerializableDocumentTests.cs

@@ -47,7 +47,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             Assert.Equal(document.Height, convertedDocument.Height);
             Assert.Equal(document.Height, convertedDocument.Height);
             Assert.Equal(document.Width, convertedDocument.Width);
             Assert.Equal(document.Width, convertedDocument.Width);
             Assert.Equal(document.Swatches, convertedDocument.Swatches);
             Assert.Equal(document.Swatches, convertedDocument.Swatches);
-            Assert.Equal(document.Layers.Select(x => x.LayerBitmap.ToByteArray()),
+            Assert.Equal(
+                document.Layers.Select(x => x.LayerBitmap.ToByteArray()),
                 convertedDocument.Layers.Select(x => x.LayerBitmap.ToByteArray()));
                 convertedDocument.Layers.Select(x => x.LayerBitmap.ToByteArray()));
         }
         }
 
 

+ 0 - 1
PixiEditorTests/ModelsTests/IO/ImporterTests.cs

@@ -11,7 +11,6 @@ namespace PixiEditorTests.ModelsTests.IO
         private readonly string testImagePath;
         private readonly string testImagePath;
 
 
         // I am not testing ImportDocument, because it's just a wrapper for BinarySerialization which is tested.
         // I am not testing ImportDocument, because it's just a wrapper for BinarySerialization which is tested.
-
         public ImporterTests()
         public ImporterTests()
         {
         {
             testImagePath = $"{Environment.CurrentDirectory}\\..\\..\\..\\ModelsTests\\IO\\TestImage.png";
             testImagePath = $"{Environment.CurrentDirectory}\\..\\..\\..\\ModelsTests\\IO\\TestImage.png";

+ 1 - 0
PixiEditorTests/ModelsTests/ImageManipulationTests/BitmapUtilsTests.cs

@@ -93,6 +93,7 @@ namespace PixiEditorTests.ModelsTests.ImageManipulationTests
             Assert.Single(colors.Where(x => x == Colors.Green));
             Assert.Single(colors.Where(x => x == Colors.Green));
             Assert.Single(colors.Where(x => x == Colors.Red));
             Assert.Single(colors.Where(x => x == Colors.Red));
             Assert.Equal(6, colors.Count(x => x.A == 0)); // 6 because layer is 4 pixels,
             Assert.Equal(6, colors.Count(x => x.A == 0)); // 6 because layer is 4 pixels,
+
             // 2 * 4 = 8, 2 other color pixels, so 8 - 2 = 6
             // 2 * 4 = 8, 2 other color pixels, so 8 - 2 = 6
         }
         }
     }
     }

+ 130 - 129
PixiEditorTests/ModelsTests/LayersTests/LayerTests.cs

@@ -1,140 +1,141 @@
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using Xunit;
-
-namespace PixiEditorTests.ModelsTests.LayersTests
-{
-    public class LayerTests
-    {
-        [Fact]
-        public void TestThatEmptyLayerGeneratesCorrectly()
-        {
-            Layer layer = new Layer("layer");
-
-            Assert.Equal("layer", layer.Name);
-            Assert.Equal(0, layer.Width);
-            Assert.Equal(0, layer.Height);
-            Assert.Equal(1, layer.LayerBitmap.PixelWidth);
-            Assert.Equal(1, layer.LayerBitmap.PixelHeight);
-        }
-
-        [Fact]
-        public void TestThatEmptyLayerWithSizeGeneratesCorrectly()
-        {
-            Layer layer = new Layer("layer", 10, 10);
-
-            Assert.Equal("layer", layer.Name);
-            Assert.Equal(10, layer.Width);
-            Assert.Equal(10, layer.Height);
-            Assert.Equal(10, layer.LayerBitmap.PixelWidth);
-            Assert.Equal(10, layer.LayerBitmap.PixelHeight);
-        }
-
-        [Fact]
-        public void TestThatLayerFromBitmapGeneratesCorrectly()
-        {
-            WriteableBitmap bmp = BitmapFactory.New(10, 10);
-
-            Layer layer = new Layer("layer", bmp);
-
-            Assert.Equal("layer", layer.Name);
-            Assert.Equal(10, layer.Width);
-            Assert.Equal(10, layer.Height);
-            Assert.Equal(10, layer.LayerBitmap.PixelWidth);
-            Assert.Equal(10, layer.LayerBitmap.PixelHeight);
-        }
-
-        [Fact]
-        public void TestThatCloneClonesCorrectly()
-        {
-            Layer layer = new Layer("test", 5, 2);
-
-            Layer clone = layer.Clone();
-
-            Assert.Equal(layer.Name, clone.Name);
-            Assert.Equal(layer.Offset, clone.Offset);
-            Assert.Equal(layer.Width, clone.Width);
-            Assert.Equal(layer.Height, clone.Height);
-            Assert.Equal(layer.MaxHeight, clone.MaxHeight);
-            Assert.Equal(layer.MaxWidth, clone.MaxWidth);
-            Assert.Equal(layer.Opacity, clone.Opacity);
-            Assert.Equal(layer.IsVisible, clone.IsVisible);
-            Assert.Equal(layer.IsRenaming, clone.IsRenaming);
-            Assert.Equal(layer.ConvertBitmapToBytes(), clone.ConvertBitmapToBytes());
-        }
-
-        [Fact]
-        public void TestThatCloneIsMakingDeepCopyOfBitmap()
-        {
-            Layer layer = new Layer("test", 5, 2);
-
-            Layer clone = layer.Clone();
-
-            clone.LayerBitmap.SetPixel(0, 0, Colors.Green); // Actually we are checking if modifying clone bitmap does not affect original
-
-            Assert.NotEqual(Colors.Green, layer.GetPixel(0, 0));
-        }
-
-        [Fact]
-        public void TestThatResizeResizesBitmap()
-        {
-            Layer layer = new Layer("layer", 1, 1);
-
-            layer.SetPixel(new Coordinates(0, 0), Colors.Black);
-
-            layer.Resize(2, 2, 2, 2);
-
-            Assert.Equal(2, layer.Width);
-            Assert.Equal(2, layer.Height);
-            Assert.Equal(2, layer.MaxWidth);
-            Assert.Equal(2, layer.MaxHeight);
-
-            for (int y = 0; y < layer.Height; y++) // 4 is new area of bitmap
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using PixiEditor.Models.Position;
+using Xunit;
+
+namespace PixiEditorTests.ModelsTests.LayersTests
+{
+    public class LayerTests
+    {
+        [Fact]
+        public void TestThatEmptyLayerGeneratesCorrectly()
+        {
+            Layer layer = new Layer("layer");
+
+            Assert.Equal("layer", layer.Name);
+            Assert.Equal(0, layer.Width);
+            Assert.Equal(0, layer.Height);
+            Assert.Equal(1, layer.LayerBitmap.PixelWidth);
+            Assert.Equal(1, layer.LayerBitmap.PixelHeight);
+        }
+
+        [Fact]
+        public void TestThatEmptyLayerWithSizeGeneratesCorrectly()
+        {
+            Layer layer = new Layer("layer", 10, 10);
+
+            Assert.Equal("layer", layer.Name);
+            Assert.Equal(10, layer.Width);
+            Assert.Equal(10, layer.Height);
+            Assert.Equal(10, layer.LayerBitmap.PixelWidth);
+            Assert.Equal(10, layer.LayerBitmap.PixelHeight);
+        }
+
+        [Fact]
+        public void TestThatLayerFromBitmapGeneratesCorrectly()
+        {
+            WriteableBitmap bmp = BitmapFactory.New(10, 10);
+
+            Layer layer = new Layer("layer", bmp);
+
+            Assert.Equal("layer", layer.Name);
+            Assert.Equal(10, layer.Width);
+            Assert.Equal(10, layer.Height);
+            Assert.Equal(10, layer.LayerBitmap.PixelWidth);
+            Assert.Equal(10, layer.LayerBitmap.PixelHeight);
+        }
+
+        [Fact]
+        public void TestThatCloneClonesCorrectly()
+        {
+            Layer layer = new Layer("test", 5, 2);
+
+            Layer clone = layer.Clone();
+
+            Assert.Equal(layer.Name, clone.Name);
+            Assert.Equal(layer.Offset, clone.Offset);
+            Assert.Equal(layer.Width, clone.Width);
+            Assert.Equal(layer.Height, clone.Height);
+            Assert.Equal(layer.MaxHeight, clone.MaxHeight);
+            Assert.Equal(layer.MaxWidth, clone.MaxWidth);
+            Assert.Equal(layer.Opacity, clone.Opacity);
+            Assert.Equal(layer.IsVisible, clone.IsVisible);
+            Assert.Equal(layer.IsRenaming, clone.IsRenaming);
+            Assert.Equal(layer.ConvertBitmapToBytes(), clone.ConvertBitmapToBytes());
+        }
+
+        [Fact]
+        public void TestThatCloneIsMakingDeepCopyOfBitmap()
+        {
+            Layer layer = new Layer("test", 5, 2);
+
+            Layer clone = layer.Clone();
+
+            clone.LayerBitmap.SetPixel(0, 0, Colors.Green); // Actually we are checking if modifying clone bitmap does not affect original
+
+            Assert.NotEqual(Colors.Green, layer.GetPixel(0, 0));
+        }
+
+        [Fact]
+        public void TestThatResizeResizesBitmap()
+        {
+            Layer layer = new Layer("layer", 1, 1);
+
+            layer.SetPixel(new Coordinates(0, 0), Colors.Black);
+
+            layer.Resize(2, 2, 2, 2);
+
+            Assert.Equal(2, layer.Width);
+            Assert.Equal(2, layer.Height);
+            Assert.Equal(2, layer.MaxWidth);
+            Assert.Equal(2, layer.MaxHeight);
+
+            // 4 is new area of bitmap
+            for (int y = 0; y < layer.Height; y++)
             {
             {
                 for (int x = 0; x < layer.Width; x++)
                 for (int x = 0; x < layer.Width; x++)
                 {
                 {
                     Assert.Equal(Colors.Black, layer.GetPixel(x, y));
                     Assert.Equal(Colors.Black, layer.GetPixel(x, y));
                 }
                 }
             }
             }
-        }
-
-        [Fact]
-        public void TestThatGetPixelReturnsTransparentIfOutOfBounds()
-        {
-            Layer layer = new Layer("layer");
-
-            Assert.Equal(0, layer.GetPixel(-1, 999).A);
-        }
-
-        [Fact]
-        public void TestThatSetPixelsSetsPixels() // This also tests if Dynamic Resize works
-        {
-            Coordinates[] pixels = { new Coordinates(4, 2), new Coordinates(0, 0), new Coordinates(15, 2) };
-
-            Layer layer = new Layer("layer");
-
-            layer.SetPixels(BitmapPixelChanges.FromSingleColoredArray(pixels, Colors.Green));
-
+        }
+
+        [Fact]
+        public void TestThatGetPixelReturnsTransparentIfOutOfBounds()
+        {
+            Layer layer = new Layer("layer");
+
+            Assert.Equal(0, layer.GetPixel(-1, 999).A);
+        }
+
+        [Fact]
+        public void TestThatSetPixelsSetsPixels() // This also tests if Dynamic Resize works
+        {
+            Coordinates[] pixels = { new Coordinates(4, 2), new Coordinates(0, 0), new Coordinates(15, 2) };
+
+            Layer layer = new Layer("layer");
+
+            layer.SetPixels(BitmapPixelChanges.FromSingleColoredArray(pixels, Colors.Green));
+
             for (int i = 0; i < pixels.Length; i++)
             for (int i = 0; i < pixels.Length; i++)
             {
             {
                 Assert.Equal(Colors.Green, layer.GetPixelWithOffset(pixels[i].X, pixels[i].Y));
                 Assert.Equal(Colors.Green, layer.GetPixelWithOffset(pixels[i].X, pixels[i].Y));
             }
             }
-        }
-
-        [Fact]
-        public void TestThatClipCanvasResizesBitmapCorrectly()
-        {
-            Layer layer = new Layer("layer", 10, 10);
-            layer.SetPixel(new Coordinates(4, 4), Colors.Blue);
-
-            layer.ClipCanvas();
-
-            Assert.Equal(1, layer.Width);
-            Assert.Equal(1, layer.Height);
-            Assert.Equal(Colors.Blue, layer.GetPixel(0, 0));
-        }
-    }
+        }
+
+        [Fact]
+        public void TestThatClipCanvasResizesBitmapCorrectly()
+        {
+            Layer layer = new Layer("layer", 10, 10);
+            layer.SetPixel(new Coordinates(4, 4), Colors.Blue);
+
+            layer.ClipCanvas();
+
+            Assert.Equal(1, layer.Width);
+            Assert.Equal(1, layer.Height);
+            Assert.Equal(Colors.Blue, layer.GetPixel(0, 0));
+        }
+    }
 }
 }

+ 1 - 0
PixiEditorTests/ModelsTests/ToolsTests/BrightnessToolTests.cs

@@ -12,6 +12,7 @@ namespace PixiEditorTests.ModelsTests.ToolsTests
         [StaTheory]
         [StaTheory]
         [InlineData(5, 12, 12, 12)]
         [InlineData(5, 12, 12, 12)]
         [InlineData(-5, 242, 242, 242)]
         [InlineData(-5, 242, 242, 242)]
+
         // If correction factor is negative, testing color will be white, otherwise black
         // If correction factor is negative, testing color will be white, otherwise black
         public void TestThatBrightnessToolChangesPixelBrightness(float correctionFactor, byte expectedR, byte expectedG, byte expectedB)
         public void TestThatBrightnessToolChangesPixelBrightness(float correctionFactor, byte expectedR, byte expectedG, byte expectedB)
         {
         {

+ 29 - 27
PixiEditorTests/ModelsTests/ToolsTests/RectangleToolTests.cs

@@ -1,28 +1,30 @@
-using System.Linq;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.Tools;
-using Xunit;
-
-namespace PixiEditorTests.ModelsTests.ToolsTests
-{
-    [Collection("Application collection")]
-    public class RectangleToolTests
-    {
-        [StaTheory]
-        [InlineData(0, 0, 2, 2)]
-        [InlineData(0, 0, 9, 9)]
-        [InlineData(5, 5, 6, 6)]
-        [InlineData(0, 0, 15, 15)]
-        public void TestThatCreateRectangleCalculatesCorrectOutlineWithOneThickness(int startX, int startY, int endX, int endY)
-        {
-            RectangleTool tool = new RectangleTool();
-
-            System.Collections.Generic.IEnumerable<Coordinates> outline = tool.CreateRectangle(new Coordinates(startX, startY),
-                new Coordinates(endX, endY), 1);
-
-            int expectedBorderPoints = (endX - startX) * 2 + (endY - startX) * 2;
-
-            Assert.Equal(expectedBorderPoints, outline.Count());
-        }
-    }
+using System.Linq;
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools.Tools;
+using Xunit;
+
+namespace PixiEditorTests.ModelsTests.ToolsTests
+{
+    [Collection("Application collection")]
+    public class RectangleToolTests
+    {
+        [StaTheory]
+        [InlineData(0, 0, 2, 2)]
+        [InlineData(0, 0, 9, 9)]
+        [InlineData(5, 5, 6, 6)]
+        [InlineData(0, 0, 15, 15)]
+        public void TestThatCreateRectangleCalculatesCorrectOutlineWithOneThickness(int startX, int startY, int endX, int endY)
+        {
+            RectangleTool tool = new RectangleTool();
+
+            System.Collections.Generic.IEnumerable<Coordinates> outline = tool.CreateRectangle(
+                new Coordinates(startX, startY),
+                new Coordinates(endX, endY),
+                1);
+
+            int expectedBorderPoints = ((endX - startX) * 2) + ((endY - startX) * 2);
+
+            Assert.Equal(expectedBorderPoints, outline.Count());
+        }
+    }
 }
 }

+ 2 - 1
PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs

@@ -155,7 +155,8 @@ namespace PixiEditorTests.ViewModelsTests
 
 
             viewModel.SelectAllCommand.Execute(null);
             viewModel.SelectAllCommand.Execute(null);
 
 
-            Assert.Equal(viewModel.BitmapManager.ActiveDocument.Width * viewModel.BitmapManager.ActiveDocument.Height,
+            Assert.Equal(
+                viewModel.BitmapManager.ActiveDocument.Width * viewModel.BitmapManager.ActiveDocument.Height,
                 viewModel.ActiveSelection.SelectedPoints.Count);
                 viewModel.ActiveSelection.SelectedPoints.Count);
         }
         }