Browse Source

Merge pull request #375 from PixiEditor/code-cleanup

Code cleanup
Krzysztof Krysiński 3 years ago
parent
commit
808b44b3ec
45 changed files with 832 additions and 1649 deletions
  1. 0 19
      PixiEditor/Exceptions/ArrayLengthMismatchException.cs
  2. 0 106
      PixiEditor/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs
  3. 0 72
      PixiEditor/Helpers/Behaviours/HintTextBehavior.cs
  4. 0 15
      PixiEditor/Helpers/Converters/BoolToBrushConverter.cs
  5. 0 34
      PixiEditor/Helpers/Converters/BrushTuple.cs
  6. 19 19
      PixiEditor/Helpers/Converters/EnumToStringConverter.cs
  7. 0 27
      PixiEditor/Helpers/Converters/FinalIsVisibleToVisiblityConverter.cs
  8. 0 27
      PixiEditor/Helpers/Converters/LayerToFinalOpacityConverter.cs
  9. 1 1
      PixiEditor/Helpers/Extensions/Int32RectHelper.cs
  10. 0 11
      PixiEditor/Helpers/Extensions/PixiParserHelper.cs
  11. 1 1
      PixiEditor/Helpers/Extensions/SKRectIHelper.cs
  12. 0 15
      PixiEditor/Helpers/Validators/SizeValidationRule.cs
  13. 0 21
      PixiEditor/Models/Controllers/BitmapChangedEventArgs.cs
  14. 1 1
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  15. 1 23
      PixiEditor/Models/DataHolders/BitmapPixelChanges.cs
  16. 1 1
      PixiEditor/Models/DataHolders/Document/Document.cs
  17. 598 577
      PixiEditor/Models/DataHolders/RangeObservableCollection.cs
  18. 100 89
      PixiEditor/Models/DataHolders/WpfObservableRangeCollection.cs
  19. 1 17
      PixiEditor/Models/Dialogs/ConfirmationDialog.cs
  20. 2 2
      PixiEditor/Models/Dialogs/ResizeDocumentDialog.cs
  21. 0 8
      PixiEditor/Models/Enums/CapType.cs
  22. 0 27
      PixiEditor/Models/ImageManipulation/BitmapUtils.cs
  23. 0 99
      PixiEditor/Models/ImageManipulation/Morphology.cs
  24. 0 31
      PixiEditor/Models/ImageManipulation/Transform.cs
  25. 0 19
      PixiEditor/Models/Position/MousePositionConverter.cs
  26. 0 41
      PixiEditor/ViewModels/MenuButtonViewModel.cs
  27. 1 1
      PixiEditor/ViewModels/SubViewModels/Main/UpdateViewModel.cs
  28. 1 1
      PixiEditor/ViewModels/ViewModelMain.cs
  29. 3 3
      PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs
  30. 0 57
      PixiEditor/Views/UserControls/MenuButton.xaml
  31. 0 46
      PixiEditor/Views/UserControls/MenuButton.xaml.cs
  32. 0 16
      PixiEditor/Views/UserControls/Rotatebox.xaml
  33. 0 71
      PixiEditor/Views/UserControls/Rotatebox.xaml.cs
  34. 0 1
      PixiEditor/Views/UserControls/SizeInput.xaml
  35. 5 5
      PixiEditorTests/ModelsTests/ControllersTests/MockedSinglePixelPenTool.cs
  36. 40 32
      PixiEditorTests/ModelsTests/ControllersTests/UndoManagerTests.cs
  37. 1 24
      PixiEditorTests/ModelsTests/DataHoldersTests/BitmapPixelChangesTests.cs
  38. 12 13
      PixiEditorTests/ModelsTests/DataHoldersTests/DocumentLayersTests.cs
  39. 16 15
      PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs
  40. 2 2
      PixiEditorTests/ModelsTests/DataHoldersTests/SelectionTests.cs
  41. 2 0
      PixiEditorTests/ModelsTests/DataHoldersTests/SurfaceTests.cs
  42. 0 39
      PixiEditorTests/ModelsTests/ImageManipulationTests/TransformTests.cs
  43. 5 1
      PixiEditorTests/ModelsTests/LayersTests/LayersTestHelper.cs
  44. 3 2
      PixiEditorTests/ModelsTests/PositionTests/CoordinatesTests.cs
  45. 16 17
      PixiEditorTests/ModelsTests/UndoTests/StorageBasedChangeTests.cs

+ 0 - 19
PixiEditor/Exceptions/ArrayLengthMismatchException.cs

@@ -1,19 +0,0 @@
-using System;
-
-namespace PixiEditor.Exceptions
-{
-    public class ArrayLengthMismatchException : Exception
-    {
-        public const string DefaultMessage = "First array length doesn't match second array length";
-
-        public ArrayLengthMismatchException()
-            : base(DefaultMessage)
-        {
-        }
-
-        public ArrayLengthMismatchException(string message)
-            : base(message)
-        {
-        }
-    }
-}

+ 0 - 106
PixiEditor/Helpers/Behaviours/AllowableCharactersTextBoxBehavior.cs

@@ -1,106 +0,0 @@
-using System;
-using System.Text.RegularExpressions;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Interactivity;
-
-namespace PixiEditor.Helpers.Behaviours
-{
-    public class AllowableCharactersTextBoxBehavior : Behavior<TextBox>
-    {
-        public static readonly DependencyProperty RegularExpressionProperty =
-            DependencyProperty.Register(
-                "RegularExpression",
-                typeof(string),
-                typeof(AllowableCharactersTextBoxBehavior),
-                new FrameworkPropertyMetadata(".*"));
-
-        public static readonly DependencyProperty MaxLengthProperty =
-            DependencyProperty.Register(
-                "MaxLength",
-                typeof(int),
-                typeof(AllowableCharactersTextBoxBehavior),
-                new FrameworkPropertyMetadata(int.MinValue));
-
-        public string RegularExpression
-        {
-            get => (string)GetValue(RegularExpressionProperty);
-            set => SetValue(RegularExpressionProperty, value);
-        }
-
-        public int MaxLength
-        {
-            get => (int)GetValue(MaxLengthProperty);
-            set => SetValue(MaxLengthProperty, value);
-        }
-
-        protected override void OnAttached()
-        {
-            base.OnAttached();
-            AssociatedObject.PreviewTextInput += OnPreviewTextInput;
-            DataObject.AddPastingHandler(AssociatedObject, OnPaste);
-        }
-
-        protected override void OnDetaching()
-        {
-            base.OnDetaching();
-            AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
-            DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
-        }
-
-        private void OnPaste(object sender, DataObjectPastingEventArgs e)
-        {
-            if (e.DataObject.GetDataPresent(DataFormats.Text))
-            {
-                string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
-
-                if (!IsValid(text, true))
-                {
-                    e.CancelCommand();
-                }
-            }
-            else
-            {
-                e.CancelCommand();
-            }
-        }
-
-        private void OnPreviewTextInput(object sender, TextCompositionEventArgs e)
-        {
-            e.Handled = !IsValid(e.Text, false);
-        }
-
-        private bool IsValid(string newText, bool paste)
-        {
-            return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);
-        }
-
-        private bool ExceedsMaxLength(string newText, bool paste)
-        {
-            if (MaxLength == 0)
-            {
-                return false;
-            }
-
-            return LengthOfModifiedText(newText, paste) > MaxLength;
-        }
-
-        private int LengthOfModifiedText(string newText, bool paste)
-        {
-            int countOfSelectedChars = AssociatedObject.SelectedText.Length;
-            int caretIndex = AssociatedObject.CaretIndex;
-            string text = AssociatedObject.Text;
-
-            if (countOfSelectedChars > 0 || paste)
-            {
-                text = text.Remove(caretIndex, countOfSelectedChars);
-                return text.Length + newText.Length;
-            }
-
-            bool insert = Keyboard.IsKeyToggled(Key.Insert);
-
-            return insert && caretIndex < text.Length ? text.Length : text.Length + newText.Length;
-        }
-    }
-}

+ 0 - 72
PixiEditor/Helpers/Behaviours/HintTextBehavior.cs

@@ -1,72 +0,0 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Interactivity;
-using System.Windows.Media;
-
-namespace PixiEditor.Helpers.Behaviours
-{
-    internal class HintTextBehavior : Behavior<TextBox>
-    {
-        // Using a DependencyProperty as the backing store for Hint.  This enables animation, styling, binding, etc...
-        public static readonly DependencyProperty HintProperty =
-            DependencyProperty.Register(
-                "Hint",
-                typeof(string),
-                typeof(HintTextBehavior),
-                new PropertyMetadata(string.Empty));
-
-        private Brush textColor;
-
-        public string Hint
-        {
-            get => (string)GetValue(HintProperty);
-            set => SetValue(HintProperty, value);
-        }
-
-        protected override void OnAttached()
-        {
-            base.OnAttached();
-            AssociatedObject.GotFocus += AssociatedObject_GotFocus;
-            AssociatedObject.LostFocus += AssociatedObject_LostFocus;
-            textColor = AssociatedObject.Foreground;
-            SetHint(true);
-        }
-
-        protected override void OnDetaching()
-        {
-            base.OnDetaching();
-            AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
-            AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
-        }
-
-        private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
-        {
-            if (string.IsNullOrEmpty(AssociatedObject.Text))
-            {
-                SetHint(true);
-            }
-        }
-
-        private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
-        {
-            if (AssociatedObject.Text == Hint)
-            {
-                SetHint(false);
-            }
-        }
-
-        private void SetHint(bool active)
-        {
-            if (active)
-            {
-                AssociatedObject.Foreground = (SolidColorBrush)new BrushConverter().ConvertFromString("#7B7B7B");
-                AssociatedObject.Text = Hint;
-            }
-            else
-            {
-                AssociatedObject.Text = string.Empty;
-                AssociatedObject.Foreground = textColor;
-            }
-        }
-    }
-}

+ 0 - 15
PixiEditor/Helpers/Converters/BoolToBrushConverter.cs

@@ -1,15 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace PixiEditor.Helpers.Converters
-{
-    public class BoolToBrushConverter
-        : SingleInstanceConverter<BoolToBrushConverter>
-    {
-        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            BrushTuple tuple = (BrushTuple)parameter;
-            return (bool)value ? tuple.FirstBrush : tuple.SecondBrush;
-        }
-    }
-}

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

@@ -1,34 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using System.Windows.Media;
-
-namespace PixiEditor.Helpers.Converters
-{
-    public class BrushTuple : NotifyableObject, ITuple
-    {
-        public object this[int index] => index switch
-        {
-            0 => FirstBrush,
-            1 => SecondBrush,
-            _ => throw new ArgumentOutOfRangeException(nameof(index))
-        };
-
-        private Brush item1;
-
-        public Brush FirstBrush
-        {
-            get => item1;
-            set => SetProperty(ref item1, value);
-        }
-
-        private Brush item2;
-
-        public Brush SecondBrush
-        {
-            get => item2;
-            set => SetProperty(ref item2, value);
-        }
-
-        public int Length => 2;
-    }
-}

+ 19 - 19
PixiEditor/Helpers/Converters/EnumToStringConverter.cs

@@ -3,27 +3,27 @@ using System;
 
 namespace PixiEditor.Helpers.Converters
 {
-  internal class EnumToStringConverter : SingleInstanceConverter<EnumToStringConverter>
-  {
-    public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
+    internal class EnumToStringConverter : SingleInstanceConverter<EnumToStringConverter>
     {
-      try
-      {
-        var type = value.GetType();
-        if (type == typeof(SizeUnit))
+        public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
         {
-          var valueCasted = (SizeUnit)value;
-          if (valueCasted == SizeUnit.Percentage)
-            return "%";
-          
-          return "px";
+            try
+            {
+                var type = value.GetType();
+                if (type == typeof(SizeUnit))
+                {
+                    var valueCasted = (SizeUnit)value;
+                    if (valueCasted == SizeUnit.Percentage)
+                        return "%";
+
+                    return "px";
+                }
+                return Enum.GetName((value.GetType()), value);
+            }
+            catch
+            {
+                return string.Empty;
+            }
         }
-        return Enum.GetName((value.GetType()), value);
-      }
-      catch
-      {
-        return string.Empty;
-      }
     }
-  }
 }

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

@@ -1,27 +0,0 @@
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.Layers;
-using PixiEditor.ViewModels;
-using System;
-using System.Globalization;
-using System.Windows;
-using System.Windows.Data;
-using System.Windows.Markup;
-
-namespace PixiEditor.Helpers.Converters
-{
-    public class FinalIsVisibleToVisiblityConverter
-        : SingleInstanceMultiValueConverter<FinalIsVisibleToVisiblityConverter>
-    {
-        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
-        {
-            BitmapManager bitmapManager = ViewModelMain.Current?.BitmapManager;
-
-            return
-                (values[0] is not Layer layer ||
-                bitmapManager.ActiveDocument is null ||
-                bitmapManager.ActiveDocument.GetFinalLayerIsVisible(layer))
-                    ? Visibility.Visible
-                    : (object)Visibility.Collapsed;
-        }
-    }
-}

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

@@ -1,27 +0,0 @@
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Layers.Utils;
-using PixiEditor.ViewModels;
-using System;
-using System.Globalization;
-
-namespace PixiEditor.Helpers.Converters
-{
-    public class LayerToFinalOpacityConverter
-        : SingleInstanceMultiValueConverter<LayerToFinalOpacityConverter>
-    {
-        public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (values.Length > 0 && values[0] is Layer layer && ViewModelMain.Current?.BitmapManager?.ActiveDocument != null)
-            {
-                return (double)LayerStructureUtils.GetFinalLayerOpacity(layer, ViewModelMain.Current.BitmapManager.ActiveDocument.LayerStructure);
-            }
-
-            return null;
-        }
-
-        public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
-        {
-            return null;
-        }
-    }
-}

+ 1 - 1
PixiEditor/Helpers/Extensions/Int32RectEx.cs → PixiEditor/Helpers/Extensions/Int32RectHelper.cs

@@ -4,7 +4,7 @@ using System.Windows;
 
 namespace PixiEditor.Helpers.Extensions
 {
-    public static class Int32RectEx
+    public static class Int32RectHelper
     {
         public static Int32Rect Intersect(this Int32Rect rect, Int32Rect other)
         {

+ 0 - 11
PixiEditor/Helpers/Extensions/PixiParserHelper.cs

@@ -1,11 +0,0 @@
-using PixiEditor.Parser;
-using SkiaSharp;
-
-namespace PixiEditor.Helpers.Extensions
-{
-    public static class PixiParserHelper
-    {
-        public static SKRectI GetRect(this SerializableLayer layer) =>
-            SKRectI.Create(layer.OffsetX, layer.OffsetY, layer.Width, layer.Height);
-    }
-}

+ 1 - 1
PixiEditor/Helpers/Extensions/SKRectIEx.cs → PixiEditor/Helpers/Extensions/SKRectIHelper.cs

@@ -3,7 +3,7 @@ using System.Windows;
 
 namespace PixiEditor.Helpers.Extensions
 {
-    public static class SKRectIEx
+    public static class SKRectIHelper
     {
         public static Int32Rect ToInt32Rect(this SKRectI rect)
         {

+ 0 - 15
PixiEditor/Helpers/Validators/SizeValidationRule.cs

@@ -1,15 +0,0 @@
-using System.Globalization;
-using System.Windows.Controls;
-
-namespace PixiEditor.Helpers.Validators
-{
-    public class SizeValidationRule : ValidationRule
-    {
-        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
-        {
-            int i = int.Parse(((string)value).Split(' ')[0]);
-
-            return new ValidationResult(i > 0, null); // Size is greater than 0
-        }
-    }
-}

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

@@ -1,21 +0,0 @@
-using PixiEditor.Models.DataHolders;
-using System;
-
-namespace PixiEditor.Models.Controllers
-{
-    public class BitmapChangedEventArgs : EventArgs
-    {
-        public BitmapChangedEventArgs(BitmapPixelChanges pixelsChanged, BitmapPixelChanges oldPixelsValues, Guid changedLayerGuid)
-        {
-            PixelsChanged = pixelsChanged;
-            OldPixelsValues = oldPixelsValues;
-            ChangedLayerGuid = changedLayerGuid;
-        }
-
-        public BitmapPixelChanges PixelsChanged { get; set; }
-
-        public BitmapPixelChanges OldPixelsValues { get; set; }
-
-        public Guid ChangedLayerGuid { get; set; }
-    }
-}

+ 1 - 1
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -13,7 +13,7 @@ namespace PixiEditor.Models.Controllers
 {
     public class BitmapOperationsUtility
     {
-        public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
+        public event EventHandler BitmapChanged;
 
         public BitmapManager Manager { get; set; }
 

+ 1 - 23
PixiEditor/Models/DataHolders/BitmapPixelChanges.cs

@@ -1,5 +1,4 @@
-using PixiEditor.Exceptions;
-using PixiEditor.Helpers.Extensions;
+using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Position;
 using SkiaSharp;
 using System;
@@ -69,27 +68,6 @@ namespace PixiEditor.Models.DataHolders
             return CombineOverride(new[] { changes1, changes2 });
         }
 
-        /// <summary>
-        ///     Builds BitmapPixelChanges using 2 same-length enumerables of coordinates and colors.
-        /// </summary>
-        public static BitmapPixelChanges FromArrays(IEnumerable<Coordinates> coordinates, IEnumerable<SKColor> color)
-        {
-            Coordinates[] coordinateArray = coordinates.ToArray();
-            SKColor[] colorArray = color.ToArray();
-            if (coordinateArray.Length != colorArray.Length)
-            {
-                throw new ArrayLengthMismatchException();
-            }
-
-            Dictionary<Coordinates, SKColor> dict = new Dictionary<Coordinates, SKColor>();
-            for (int i = 0; i < coordinateArray.Length; i++)
-            {
-                dict.Add(coordinateArray[i], colorArray[i]);
-            }
-
-            return new BitmapPixelChanges(dict);
-        }
-
         public BitmapPixelChanges WithoutTransparentPixels()
         {
             return new BitmapPixelChanges(ChangedPixels.Where(x => x.Value.Alpha > 0).ToDictionary(y => y.Key, y => y.Value));

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

@@ -184,7 +184,7 @@ namespace PixiEditor.Models.DataHolders
 
             MoveOffsets(layersToCenter, emptyBounds, moveVector);
 
-            List <Guid> guids = layersToCenter.Select(x => x.GuidValue).ToList();
+            List<Guid> guids = layersToCenter.Select(x => x.GuidValue).ToList();
             UndoManager.AddUndoChange(
                 new Change(
                     MoveOffsetsProcess,

+ 598 - 577
PixiEditor/Models/DataHolders/RangeObservableCollection.cs

@@ -9,658 +9,679 @@ using System.Linq;
 
 namespace PixiEditor.Models.DataHolders
 {
-  // Licensed to the .NET Foundation under one or more agreements.
-  // The .NET Foundation licenses this file to you under the MIT license.
-  // See the LICENSE file in the project root for more information.
-  /// <summary>
-  /// Implementation of a dynamic data collection based on generic Collection&lt;T&gt;,
-  /// implementing INotifyCollectionChanged to notify listeners
-  /// when items get added, removed or the whole list is refreshed.
-  /// </summary>
-  public class RangeObservableCollection<T> : ObservableCollection<T>
-  {
-    //------------------------------------------------------
-    //
-    //  Private Fields
-    //
-    //------------------------------------------------------
-
-    #region Private Fields    
-    [NonSerialized]
-    private DeferredEventsCollection? _deferredEvents;
-    #endregion Private Fields
-
-
-    //------------------------------------------------------
-    //
-    //  Constructors
-    //
-    //------------------------------------------------------
-
-    #region Constructors
-    /// <summary>
-    /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.
-    /// </summary>
-    public RangeObservableCollection() { }
+    // Licensed to the .NET Foundation under one or more agreements.
+    // The .NET Foundation licenses this file to you under the MIT license.
+    // See the LICENSE file in the project root for more information.
 
     /// <summary>
-    /// Initializes a new instance of the ObservableCollection class that contains
-    /// elements copied from the specified collection and has sufficient capacity
-    /// to accommodate the number of elements copied.
+    /// Implementation of a dynamic data collection based on generic Collection&lt;T&gt;,
+    /// implementing INotifyCollectionChanged to notify listeners
+    /// when items get added, removed or the whole list is refreshed.
     /// </summary>
-    /// <param name="collection">The collection whose elements are copied to the new list.</param>
-    /// <remarks>
-    /// The elements are copied onto the ObservableCollection in the
-    /// same order they are read by the enumerator of the collection.
-    /// </remarks>
-    /// <exception cref="ArgumentNullException"> collection is a null reference </exception>
-    public RangeObservableCollection(IEnumerable<T> collection) : base(collection) { }
+    public class RangeObservableCollection<T> : ObservableCollection<T>
+    {
+        //------------------------------------------------------
+        //
+        //  Private Fields
+        //
+        //------------------------------------------------------
+
+        #region Private Fields    
+        [NonSerialized]
+        private DeferredEventsCollection _deferredEvents;
+        #endregion Private Fields
+
+
+        //------------------------------------------------------
+        //
+        //  Constructors
+        //
+        //------------------------------------------------------
+
+        #region Constructors
+
+        /// <summary>
+        /// Initializes a new instance of ObservableCollection that is empty and has default initial capacity.
+        /// </summary>
+        public RangeObservableCollection() { }
+
+        /// <summary>
+        /// Initializes a new instance of the ObservableCollection class that contains
+        /// elements copied from the specified collection and has sufficient capacity
+        /// to accommodate the number of elements copied.
+        /// </summary>
+        /// <param name="collection">The collection whose elements are copied to the new list.</param>
+        /// <remarks>
+        /// The elements are copied onto the ObservableCollection in the
+        /// same order they are read by the enumerator of the collection.
+        /// </remarks>
+        /// <exception cref="ArgumentNullException"> collection is a null reference </exception>
+        public RangeObservableCollection(IEnumerable<T> collection) : base(collection) { }
+
+        /// <summary>
+        /// Initializes a new instance of the ObservableCollection class
+        /// that contains elements copied from the specified list
+        /// </summary>
+        /// <param name="list">The list whose elements are copied to the new list.</param>
+        /// <remarks>
+        /// The elements are copied onto the ObservableCollection in the
+        /// same order they are read by the enumerator of the list.
+        /// </remarks>
+        /// <exception cref="ArgumentNullException"> list is a null reference </exception>
+        public RangeObservableCollection(List<T> list) : base(list) { }
+
+        #endregion Constructors
+
+        //------------------------------------------------------
+        //
+        //  Public Properties
+        //
+        //------------------------------------------------------
+
+        #region Public Properties
+#pragma warning disable SA1306 // Field names should begin with lower-case letter
+        EqualityComparer<T> _Comparer;
+#pragma warning restore SA1306 // Field names should begin with lower-case letter
+        public EqualityComparer<T> Comparer
+        {
+            get => _Comparer ??= EqualityComparer<T>.Default;
+            private set => _Comparer = value;
+        }
 
-    /// <summary>
-    /// Initializes a new instance of the ObservableCollection class
-    /// that contains elements copied from the specified list
-    /// </summary>
-    /// <param name="list">The list whose elements are copied to the new list.</param>
-    /// <remarks>
-    /// The elements are copied onto the ObservableCollection in the
-    /// same order they are read by the enumerator of the list.
-    /// </remarks>
-    /// <exception cref="ArgumentNullException"> list is a null reference </exception>
-    public RangeObservableCollection(List<T> list) : base(list) { }
+        /// <summary>
+        /// Gets or sets a value indicating whether this collection acts as a <see cref="HashSet{T}"/>,
+        /// disallowing duplicate items, based on <see cref="Comparer"/>.
+        /// This might indeed consume background performance, but in the other hand,
+        /// it will pay off in UI performance as less required UI updates are required.
+        /// </summary>
+        public bool AllowDuplicates { get; set; } = true;
+
+        #endregion Public Properties
+
+        //------------------------------------------------------
+        //
+        //  Public Methods
+        //
+        //------------------------------------------------------
+
+        #region Public Methods
+
+        /// <summary>
+        /// Adds the elements of the specified collection to the end of the <see cref="ObservableCollection{T}"/>.
+        /// </summary>
+        /// <param name="collection">
+        /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
+        /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
+        /// </param>
+        /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+        public void AddRange(IEnumerable<T> collection)
+        {
+            InsertRange(Count, collection);
+        }
 
-    #endregion Constructors
+        /// <summary>
+        /// Inserts the elements of a collection into the <see cref="ObservableCollection{T}"/> at the specified index.
+        /// </summary>
+        /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
+        /// <param name="collection">The collection whose elements should be inserted into the List<T>.
+        /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.</param>                
+        /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not in the collection range.</exception>
+        public void InsertRange(int index, IEnumerable<T> collection)
+        {
+            if (collection == null)
+                throw new ArgumentNullException(nameof(collection));
+            if (index < 0)
+                throw new ArgumentOutOfRangeException(nameof(index));
+            if (index > Count)
+                throw new ArgumentOutOfRangeException(nameof(index));
+
+            if (!AllowDuplicates)
+            {
+                collection =
+                  collection
+                  .Distinct(Comparer)
+                  .Where(item => !Items.Contains(item, Comparer))
+                  .ToList();
+            }
+
+            if (collection is ICollection<T> countable)
+            {
+                if (countable.Count == 0)
+                    return;
+            }
+            else if (!collection.Any())
+            {
+                return;
+            }
 
-    //------------------------------------------------------
-    //
-    //  Public Properties
-    //
-    //------------------------------------------------------
+            CheckReentrancy();
 
-    #region Public Properties
-    EqualityComparer<T>? _Comparer;
-    public EqualityComparer<T> Comparer
-    {
-      get => _Comparer ??= EqualityComparer<T>.Default;
-      private set => _Comparer = value;
-    }
+            //expand the following couple of lines when adding more constructors.
+            var target = (List<T>)Items;
+            target.InsertRange(index, collection);
 
-    /// <summary>
-    /// Gets or sets a value indicating whether this collection acts as a <see cref="HashSet{T}"/>,
-    /// disallowing duplicate items, based on <see cref="Comparer"/>.
-    /// This might indeed consume background performance, but in the other hand,
-    /// it will pay off in UI performance as less required UI updates are required.
-    /// </summary>
-    public bool AllowDuplicates { get; set; } = true;
+            OnEssentialPropertiesChanged();
 
-    #endregion Public Properties
+            if (!(collection is IList list))
+                list = new List<T>(collection);
 
-    //------------------------------------------------------
-    //
-    //  Public Methods
-    //
-    //------------------------------------------------------
+            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list, index));
+        }
 
-    #region Public Methods
 
-    /// <summary>
-    /// Adds the elements of the specified collection to the end of the <see cref="ObservableCollection{T}"/>.
-    /// </summary>
-    /// <param name="collection">
-    /// The collection whose elements should be added to the end of the <see cref="ObservableCollection{T}"/>.
-    /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.
-    /// </param>
-    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
-    public void AddRange(IEnumerable<T> collection)
-    {
-      InsertRange(Count, collection);
-    }
+        /// <summary> 
+        /// Removes the first occurence of each item in the specified collection from the <see cref="ObservableCollection{T}"/>.
+        /// </summary>
+        /// <param name="collection">The items to remove.</param>        
+        /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+        public void RemoveRange(IEnumerable<T> collection)
+        {
+            if (collection == null)
+                throw new ArgumentNullException(nameof(collection));
 
-    /// <summary>
-    /// Inserts the elements of a collection into the <see cref="ObservableCollection{T}"/> at the specified index.
-    /// </summary>
-    /// <param name="index">The zero-based index at which the new elements should be inserted.</param>
-    /// <param name="collection">The collection whose elements should be inserted into the List<T>.
-    /// The collection itself cannot be null, but it can contain elements that are null, if type T is a reference type.</param>                
-    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
-    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is not in the collection range.</exception>
-    public void InsertRange(int index, IEnumerable<T> collection)
-    {
-      if (collection == null)
-        throw new ArgumentNullException(nameof(collection));
-      if (index < 0)
-        throw new ArgumentOutOfRangeException(nameof(index));
-      if (index > Count)
-        throw new ArgumentOutOfRangeException(nameof(index));
-
-      if (!AllowDuplicates)
-        collection =
-          collection
-          .Distinct(Comparer)
-          .Where(item => !Items.Contains(item, Comparer))
-          .ToList();
-
-      if (collection is ICollection<T> countable)
-      {
-        if (countable.Count == 0)
-          return;
-      }
-      else if (!collection.Any())
-        return;
-
-      CheckReentrancy();
-
-      //expand the following couple of lines when adding more constructors.
-      var target = (List<T>)Items;
-      target.InsertRange(index, collection);
-
-      OnEssentialPropertiesChanged();
-
-      if (!(collection is IList list))
-        list = new List<T>(collection);
-
-      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, list, index));
-    }
+            if (Count == 0)
+            {
+                return;
+            }
+            else if (collection is ICollection<T> countable)
+            {
+                if (countable.Count == 0)
+                {
+                    return;
+                }
+                else if (countable.Count == 1)
+                {
+                    using (IEnumerator<T> enumerator = countable.GetEnumerator())
+                    {
+                        enumerator.MoveNext();
+                        Remove(enumerator.Current);
+                        return;
+                    }
+                }
+            }
+            else if (!collection.Any())
+            {
+                return;
+            }
 
+            CheckReentrancy();
 
-    /// <summary> 
-    /// Removes the first occurence of each item in the specified collection from the <see cref="ObservableCollection{T}"/>.
-    /// </summary>
-    /// <param name="collection">The items to remove.</param>        
-    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
-    public void RemoveRange(IEnumerable<T> collection)
-    {
-      if (collection == null)
-        throw new ArgumentNullException(nameof(collection));
-
-      if (Count == 0)
-        return;
-      else if (collection is ICollection<T> countable)
-      {
-        if (countable.Count == 0)
-          return;
-        else if (countable.Count == 1)
-          using (IEnumerator<T> enumerator = countable.GetEnumerator())
-          {
-            enumerator.MoveNext();
-            Remove(enumerator.Current);
-            return;
-          }
-      }
-      else if (!collection.Any())
-        return;
-
-      CheckReentrancy();
-
-      var clusters = new Dictionary<int, List<T>>();
-      var lastIndex = -1;
-      List<T>? lastCluster = null;
-      foreach (T item in collection)
-      {
-        var index = IndexOf(item);
-        if (index < 0)
-          continue;
-
-        Items.RemoveAt(index);
-
-        if (lastIndex == index && lastCluster != null)
-          lastCluster.Add(item);
-        else
-          clusters[lastIndex = index] = lastCluster = new List<T> { item };
-      }
-
-      OnEssentialPropertiesChanged();
-
-      if (Count == 0)
-        OnCollectionReset();
-      else
-        foreach (KeyValuePair<int, List<T>> cluster in clusters)
-          OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster.Value, cluster.Key));
+            var clusters = new Dictionary<int, List<T>>();
+            var lastIndex = -1;
+            List<T> lastCluster = null;
+            foreach (T item in collection)
+            {
+                var index = IndexOf(item);
+                if (index < 0)
+                    continue;
 
-    }
+                Items.RemoveAt(index);
 
-    /// <summary>
-    /// Iterates over the collection and removes all items that satisfy the specified match.
-    /// </summary>
-    /// <remarks>The complexity is O(n).</remarks>
-    /// <param name="match"></param>
-    /// <returns>Returns the number of elements that where </returns>
-    /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
-    public int RemoveAll(Predicate<T> match)
-    {
-      return RemoveAll(0, Count, match);
-    }
+                if (lastIndex == index && lastCluster != null)
+                    lastCluster.Add(item);
+                else
+                    clusters[lastIndex = index] = lastCluster = new List<T> { item };
+            }
 
-    /// <summary>
-    /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.
-    /// </summary>
-    /// <remarks>The complexity is O(n).</remarks>
-    /// <param name="index">The index of where to start performing the search.</param>
-    /// <param name="count">The number of items to iterate on.</param>
-    /// <param name="match"></param>
-    /// <returns>Returns the number of elements that where </returns>
-    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
-    /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
-    /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
-    public int RemoveAll(int index, int count, Predicate<T> match)
-    {
-      if (index < 0)
-        throw new ArgumentOutOfRangeException(nameof(index));
-      if (count < 0)
-        throw new ArgumentOutOfRangeException(nameof(count));
-      if (index + count > Count)
-        throw new ArgumentOutOfRangeException(nameof(index));
-      if (match == null)
-        throw new ArgumentNullException(nameof(match));
-
-      if (Count == 0)
-        return 0;
-
-      List<T>? cluster = null;
-      var clusterIndex = -1;
-      var removedCount = 0;
-
-      using (BlockReentrancy())
-      using (DeferEvents())
-      {
-        for (var i = 0; i < count; i++, index++)
-        {
-          T item = Items[index];
-          if (match(item))
-          {
-            Items.RemoveAt(index);
-            removedCount++;
+            OnEssentialPropertiesChanged();
 
-            if (clusterIndex == index)
+            if (Count == 0)
             {
-              Debug.Assert(cluster != null);
-              cluster!.Add(item);
+                OnCollectionReset();
             }
             else
             {
-              cluster = new List<T> { item };
-              clusterIndex = index;
+                foreach (KeyValuePair<int, List<T>> cluster in clusters)
+                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster.Value, cluster.Key));
             }
 
-            index--;
-          }
-          else if (clusterIndex > -1)
-          {
-            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
-            clusterIndex = -1;
-            cluster = null;
-          }
         }
 
-        if (clusterIndex > -1)
-          OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
-      }
+        /// <summary>
+        /// Iterates over the collection and removes all items that satisfy the specified match.
+        /// </summary>
+        /// <remarks>The complexity is O(n).</remarks>
+        /// <returns>Returns the number of elements that where </returns>
+        /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
+        public int RemoveAll(Predicate<T> match)
+        {
+            return RemoveAll(0, Count, match);
+        }
 
-      if (removedCount > 0)
-        OnEssentialPropertiesChanged();
+        /// <summary>
+        /// Iterates over the specified range within the collection and removes all items that satisfy the specified match.
+        /// </summary>
+        /// <remarks>The complexity is O(n).</remarks>
+        /// <param name="index">The index of where to start performing the search.</param>
+        /// <param name="count">The number of items to iterate on.</param>
+        /// <returns>Returns the number of elements that where </returns>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="match"/> is null.</exception>
+        public int RemoveAll(int index, int count, Predicate<T> match)
+        {
+            if (index < 0)
+                throw new ArgumentOutOfRangeException(nameof(index));
+            if (count < 0)
+                throw new ArgumentOutOfRangeException(nameof(count));
+            if (index + count > Count)
+                throw new ArgumentOutOfRangeException(nameof(index));
+            if (match == null)
+                throw new ArgumentNullException(nameof(match));
+
+            if (Count == 0)
+                return 0;
+
+            List<T> cluster = null;
+            var clusterIndex = -1;
+            var removedCount = 0;
+
+            using (BlockReentrancy())
+            using (DeferEvents())
+            {
+                for (var i = 0; i < count; i++, index++)
+                {
+                    T item = Items[index];
+                    if (match(item))
+                    {
+                        Items.RemoveAt(index);
+                        removedCount++;
+
+                        if (clusterIndex == index)
+                        {
+                            Debug.Assert(cluster != null);
+                            cluster!.Add(item);
+                        }
+                        else
+                        {
+                            cluster = new List<T> { item };
+                            clusterIndex = index;
+                        }
+
+                        index--;
+                    }
+                    else if (clusterIndex > -1)
+                    {
+                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
+                        clusterIndex = -1;
+                        cluster = null;
+                    }
+                }
+
+                if (clusterIndex > -1)
+                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, cluster, clusterIndex));
+            }
 
-      return removedCount;
-    }
+            if (removedCount > 0)
+                OnEssentialPropertiesChanged();
 
-    /// <summary>
-    /// Removes a range of elements from the <see cref="ObservableCollection{T}"/>>.
-    /// </summary>
-    /// <param name="index">The zero-based starting index of the range of elements to remove.</param>
-    /// <param name="count">The number of elements to remove.</param>
-    /// <exception cref="ArgumentOutOfRangeException">The specified range is exceeding the collection.</exception>
-    public void RemoveRange(int index, int count)
-    {
-      if (index < 0)
-        throw new ArgumentOutOfRangeException(nameof(index));
-      if (count < 0)
-        throw new ArgumentOutOfRangeException(nameof(count));
-      if (index + count > Count)
-        throw new ArgumentOutOfRangeException(nameof(index));
+            return removedCount;
+        }
 
-      if (count == 0)
-        return;
+        /// <summary>
+        /// Removes a range of elements from the <see cref="ObservableCollection{T}"/>>.
+        /// </summary>
+        /// <param name="index">The zero-based starting index of the range of elements to remove.</param>
+        /// <param name="count">The number of elements to remove.</param>
+        /// <exception cref="ArgumentOutOfRangeException">The specified range is exceeding the collection.</exception>
+        public void RemoveRange(int index, int count)
+        {
+            if (index < 0)
+                throw new ArgumentOutOfRangeException(nameof(index));
+            if (count < 0)
+                throw new ArgumentOutOfRangeException(nameof(count));
+            if (index + count > Count)
+                throw new ArgumentOutOfRangeException(nameof(index));
 
-      if (count == 1)
-      {
-        RemoveItem(index);
-        return;
-      }
+            if (count == 0)
+                return;
 
-      //Items will always be List<T>, see constructors
-      var items = (List<T>)Items;
-      List<T> removedItems = items.GetRange(index, count);
+            if (count == 1)
+            {
+                RemoveItem(index);
+                return;
+            }
 
-      CheckReentrancy();
+            //Items will always be List<T>, see constructors
+            var items = (List<T>)Items;
+            List<T> removedItems = items.GetRange(index, count);
 
-      items.RemoveRange(index, count);
+            CheckReentrancy();
 
-      OnEssentialPropertiesChanged();
+            items.RemoveRange(index, count);
 
-      if (Count == 0)
-        OnCollectionReset();
-      else
-        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, index));
-    }
+            OnEssentialPropertiesChanged();
 
-    /// <summary> 
-    /// Clears the current collection and replaces it with the specified collection,
-    /// using <see cref="Comparer"/>.
-    /// </summary>             
-    /// <param name="collection">The items to fill the collection with, after clearing it.</param>
-    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
-    public void ReplaceRange(IEnumerable<T> collection)
-    {
-      ReplaceRange(0, Count, collection);
-    }
+            if (Count == 0)
+                OnCollectionReset();
+            else
+                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItems, index));
+        }
 
-    /// <summary>
-    /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.
-    /// </summary>
-    /// <param name="index">The index of where to start the replacement.</param>
-    /// <param name="count">The number of items to be replaced.</param>
-    /// <param name="collection">The collection to insert in that location.</param>
-    /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
-    /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
-    /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
-    /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
-    public void ReplaceRange(int index, int count, IEnumerable<T> collection)
-    {
-      if (index < 0)
-        throw new ArgumentOutOfRangeException(nameof(index));
-      if (count < 0)
-        throw new ArgumentOutOfRangeException(nameof(count));
-      if (index + count > Count)
-        throw new ArgumentOutOfRangeException(nameof(index));
-
-      if (collection == null)
-        throw new ArgumentNullException(nameof(collection));
-
-      if (!AllowDuplicates)
-        collection =
-          collection
-          .Distinct(Comparer)
-          .ToList();
-
-      if (collection is ICollection<T> countable)
-      {
-        if (countable.Count == 0)
+        /// <summary> 
+        /// Clears the current collection and replaces it with the specified collection,
+        /// using <see cref="Comparer"/>.
+        /// </summary>             
+        /// <param name="collection">The items to fill the collection with, after clearing it.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+        public void ReplaceRange(IEnumerable<T> collection)
         {
-          RemoveRange(index, count);
-          return;
+            ReplaceRange(0, Count, collection);
         }
-      }
-      else if (!collection.Any())
-      {
-        RemoveRange(index, count);
-        return;
-      }
-
-      if (index + count == 0)
-      {
-        InsertRange(0, collection);
-        return;
-      }
-
-      if (!(collection is IList<T> list))
-        list = new List<T>(collection);
-
-      using (BlockReentrancy())
-      using (DeferEvents())
-      {
-        var rangeCount = index + count;
-        var addedCount = list.Count;
-
-        var changesMade = false;
-        List<T>?
-          newCluster = null,
-          oldCluster = null;
-
-
-        int i = index;
-        for (; i < rangeCount && i - index < addedCount; i++)
+
+        /// <summary>
+        /// Removes the specified range and inserts the specified collection in its position, leaving equal items in equal positions intact.
+        /// </summary>
+        /// <param name="index">The index of where to start the replacement.</param>
+        /// <param name="count">The number of items to be replaced.</param>
+        /// <param name="collection">The collection to insert in that location.</param>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is out of range.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="collection"/> is null.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
+        public void ReplaceRange(int index, int count, IEnumerable<T> collection)
         {
-          //parallel position
-          T old = this[i], @new = list[i - index];
-          if (Comparer.Equals(old, @new))
-          {
-            OnRangeReplaced(i, newCluster!, oldCluster!);
-            continue;
-          }
-          else
-          {
-            Items[i] = @new;
-
-            if (newCluster == null)
+            if (index < 0)
+                throw new ArgumentOutOfRangeException(nameof(index));
+            if (count < 0)
+                throw new ArgumentOutOfRangeException(nameof(count));
+            if (index + count > Count)
+                throw new ArgumentOutOfRangeException(nameof(index));
+
+            if (collection == null)
+                throw new ArgumentNullException(nameof(collection));
+
+            if (!AllowDuplicates)
             {
-              Debug.Assert(oldCluster == null);
-              newCluster = new List<T> { @new };
-              oldCluster = new List<T> { old };
+                collection =
+                  collection
+                  .Distinct(Comparer)
+                  .ToList();
             }
-            else
+
+            if (collection is ICollection<T> countable)
+            {
+                if (countable.Count == 0)
+                {
+                    RemoveRange(index, count);
+                    return;
+                }
+            }
+            else if (!collection.Any())
             {
-              newCluster.Add(@new);
-              oldCluster!.Add(old);
+                RemoveRange(index, count);
+                return;
             }
 
-            changesMade = true;
-          }
-        }
+            if (index + count == 0)
+            {
+                InsertRange(0, collection);
+                return;
+            }
 
-        OnRangeReplaced(i, newCluster!, oldCluster!);
+            if (!(collection is IList<T> list))
+                list = new List<T>(collection);
 
-        //exceeding position
-        if (count != addedCount)
-        {
-          var items = (List<T>)Items;
-          if (count > addedCount)
-          {
-            var removedCount = rangeCount - addedCount;
-            T[] removed = new T[removedCount];
-            items.CopyTo(i, removed, 0, removed.Length);
-            items.RemoveRange(i, removedCount);
-            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, i));
-          }
-          else
-          {
-            var k = i - index;
-            T[] added = new T[addedCount - k];
-            for (int j = k; j < addedCount; j++)
+            using (BlockReentrancy())
+            using (DeferEvents())
             {
-              T @new = list[j];
-              added[j - k] = @new;
+                var rangeCount = index + count;
+                var addedCount = list.Count;
+
+                var changesMade = false;
+                List<T>
+                  newCluster = null,
+                  oldCluster = null;
+
+
+                int i = index;
+                for (; i < rangeCount && i - index < addedCount; i++)
+                {
+                    //parallel position
+                    T old = this[i], @new = list[i - index];
+                    if (Comparer.Equals(old, @new))
+                    {
+                        OnRangeReplaced(i, newCluster!, oldCluster!);
+                        continue;
+                    }
+                    else
+                    {
+                        Items[i] = @new;
+
+                        if (newCluster == null)
+                        {
+                            Debug.Assert(oldCluster == null);
+                            newCluster = new List<T> { @new };
+                            oldCluster = new List<T> { old };
+                        }
+                        else
+                        {
+                            newCluster.Add(@new);
+                            oldCluster!.Add(old);
+                        }
+
+                        changesMade = true;
+                    }
+                }
+
+                OnRangeReplaced(i, newCluster!, oldCluster!);
+
+                //exceeding position
+                if (count != addedCount)
+                {
+                    var items = (List<T>)Items;
+                    if (count > addedCount)
+                    {
+                        var removedCount = rangeCount - addedCount;
+                        T[] removed = new T[removedCount];
+                        items.CopyTo(i, removed, 0, removed.Length);
+                        items.RemoveRange(i, removedCount);
+                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed, i));
+                    }
+                    else
+                    {
+                        var k = i - index;
+                        T[] added = new T[addedCount - k];
+                        for (int j = k; j < addedCount; j++)
+                        {
+                            T @new = list[j];
+                            added[j - k] = @new;
+                        }
+                        items.InsertRange(i, added);
+                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));
+                    }
+
+                    OnEssentialPropertiesChanged();
+                }
+                else if (changesMade)
+                {
+                    OnIndexerPropertyChanged();
+                }
             }
-            items.InsertRange(i, added);
-            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added, i));
-          }
-
-          OnEssentialPropertiesChanged();
-        }
-        else if (changesMade)
-        {
-          OnIndexerPropertyChanged();
         }
-      }
-    }
 
-    #endregion Public Methods
+        #endregion Public Methods
 
 
-    //------------------------------------------------------
-    //
-    //  Protected Methods
-    //
-    //------------------------------------------------------
+        //------------------------------------------------------
+        //
+        //  Protected Methods
+        //
+        //------------------------------------------------------
 
-    #region Protected Methods
+        #region Protected Methods
 
-    /// <summary>
-    /// Called by base class Collection&lt;T&gt; when the list is being cleared;
-    /// raises a CollectionChanged event to any listeners.
-    /// </summary>
-    protected override void ClearItems()
-    {
-      if (Count == 0)
-        return;
+        /// <summary>
+        /// Called by base class Collection&lt;T&gt; when the list is being cleared;
+        /// raises a CollectionChanged event to any listeners.
+        /// </summary>
+        protected override void ClearItems()
+        {
+            if (Count == 0)
+                return;
 
-      CheckReentrancy();
-      base.ClearItems();
-      OnEssentialPropertiesChanged();
-      OnCollectionReset();
-    }
+            CheckReentrancy();
+            base.ClearItems();
+            OnEssentialPropertiesChanged();
+            OnCollectionReset();
+        }
 
-    /// <inheritdoc/>
-    protected override void InsertItem(int index, T item)
-    {
-      if (!AllowDuplicates && Items.Contains(item))
-        return;
+        /// <inheritdoc/>
+        protected override void InsertItem(int index, T item)
+        {
+            if (!AllowDuplicates && Items.Contains(item))
+                return;
 
-      base.InsertItem(index, item);
-    }
+            base.InsertItem(index, item);
+        }
 
-    /// <inheritdoc/>
-    protected override void SetItem(int index, T item)
-    {
-      if (AllowDuplicates)
-      {
-        if (Comparer.Equals(this[index], item))
-          return;
-      }
-      else
-        if (Items.Contains(item, Comparer))
-        return;
-
-      CheckReentrancy();
-      T oldItem = this[index];
-      base.SetItem(index, item);
-
-      OnIndexerPropertyChanged();
-      OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem!, item!, index);
-    }
+        /// <inheritdoc/>
+        protected override void SetItem(int index, T item)
+        {
+            if (AllowDuplicates)
+            {
+                if (Comparer.Equals(this[index], item))
+                    return;
+            }
+            else if (Items.Contains(item, Comparer))
+            {
+                return;
+            }
 
-    /// <summary>
-    /// Raise CollectionChanged event to any listeners.
-    /// Properties/methods modifying this ObservableCollection will raise
-    /// a collection changed event through this virtual method.
-    /// </summary>
-    /// <remarks>
-    /// When overriding this method, either call its base implementation
-    /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
-    /// </remarks>
-    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
-    {
-      if (_deferredEvents != null)
-      {
-        _deferredEvents.Add(e);
-        return;
-      }
-      base.OnCollectionChanged(e);
-    }
+            CheckReentrancy();
+            T oldItem = this[index];
+            base.SetItem(index, item);
+
+            OnIndexerPropertyChanged();
+            OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem!, item!, index);
+        }
 
-    protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);
+        /// <summary>
+        /// Raise CollectionChanged event to any listeners.
+        /// Properties/methods modifying this ObservableCollection will raise
+        /// a collection changed event through this virtual method.
+        /// </summary>
+        /// <remarks>
+        /// When overriding this method, either call its base implementation
+        /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
+        /// </remarks>
+        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+        {
+            if (_deferredEvents != null)
+            {
+                _deferredEvents.Add(e);
+                return;
+            }
+            base.OnCollectionChanged(e);
+        }
 
-    #endregion Protected Methods
+        protected virtual IDisposable DeferEvents() => new DeferredEventsCollection(this);
 
+        #endregion Protected Methods
 
-    //------------------------------------------------------
-    //
-    //  Private Methods
-    //
-    //------------------------------------------------------
 
-    #region Private Methods
+        //------------------------------------------------------
+        //
+        //  Private Methods
+        //
+        //------------------------------------------------------
 
-    /// <summary>
-    /// Helper to raise Count property and the Indexer property.
-    /// </summary>
-    void OnEssentialPropertiesChanged()
-    {
-      OnPropertyChanged(EventArgsCache.CountPropertyChanged);
-      OnIndexerPropertyChanged();
-    }
+        #region Private Methods
 
-    /// <summary>
-    /// /// Helper to raise a PropertyChanged event for the Indexer property
-    /// /// </summary>
-    void OnIndexerPropertyChanged() =>
-     OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
+        /// <summary>
+        /// Helper to raise Count property and the Indexer property.
+        /// </summary>
+        void OnEssentialPropertiesChanged()
+        {
+            OnPropertyChanged(EventArgsCache.CountPropertyChanged);
+            OnIndexerPropertyChanged();
+        }
 
-    /// <summary>
-    /// Helper to raise CollectionChanged event to any listeners
-    /// </summary>
-    void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
-     OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
+        /// <summary>
+        /// /// Helper to raise a PropertyChanged event for the Indexer property
+        /// /// </summary>
+        void OnIndexerPropertyChanged() =>
+         OnPropertyChanged(EventArgsCache.IndexerPropertyChanged);
+
+        /// <summary>
+        /// Helper to raise CollectionChanged event to any listeners
+        /// </summary>
+        void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
+         OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
+
+        /// <summary>
+        /// Helper to raise CollectionChanged event with action == Reset to any listeners
+        /// </summary>
+        void OnCollectionReset() =>
+         OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
+
+        /// <summary>
+        /// Helper to raise event for clustered action and clear cluster.
+        /// </summary>
+        /// <param name="followingItemIndex">The index of the item following the replacement block.</param>
+        //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer),
+        //move when supported language version updated.
+        void OnRangeReplaced(int followingItemIndex, ICollection<T> newCluster, ICollection<T> oldCluster)
+        {
+            if (oldCluster == null || oldCluster.Count == 0)
+            {
+                Debug.Assert(newCluster == null || newCluster.Count == 0);
+                return;
+            }
 
-    /// <summary>
-    /// Helper to raise CollectionChanged event with action == Reset to any listeners
-    /// </summary>
-    void OnCollectionReset() =>
-     OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
+            OnCollectionChanged(
+              new NotifyCollectionChangedEventArgs(
+                NotifyCollectionChangedAction.Replace,
+                new List<T>(newCluster),
+                new List<T>(oldCluster),
+                followingItemIndex - oldCluster.Count));
 
-    /// <summary>
-    /// Helper to raise event for clustered action and clear cluster.
-    /// </summary>
-    /// <param name="followingItemIndex">The index of the item following the replacement block.</param>
-    /// <param name="newCluster"></param>
-    /// <param name="oldCluster"></param>
-    //TODO should have really been a local method inside ReplaceRange(int index, int count, IEnumerable<T> collection, IEqualityComparer<T> comparer),
-    //move when supported language version updated.
-    void OnRangeReplaced(int followingItemIndex, ICollection<T> newCluster, ICollection<T> oldCluster)
-    {
-      if (oldCluster == null || oldCluster.Count == 0)
-      {
-        Debug.Assert(newCluster == null || newCluster.Count == 0);
-        return;
-      }
-
-      OnCollectionChanged(
-        new NotifyCollectionChangedEventArgs(
-          NotifyCollectionChangedAction.Replace,
-          new List<T>(newCluster),
-          new List<T>(oldCluster),
-          followingItemIndex - oldCluster.Count));
-
-      oldCluster.Clear();
-      newCluster.Clear();
-    }
+            oldCluster.Clear();
+            newCluster.Clear();
+        }
 
-    #endregion Private Methods
+        #endregion Private Methods
 
-    //------------------------------------------------------
-    //
-    //  Private Types
-    //
-    //------------------------------------------------------
+        //------------------------------------------------------
+        //
+        //  Private Types
+        //
+        //------------------------------------------------------
 
-    #region Private Types
-    sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
-    {
-      readonly RangeObservableCollection<T> _collection;
-      public DeferredEventsCollection(RangeObservableCollection<T> collection)
-      {
-        Debug.Assert(collection != null);
-        Debug.Assert(collection._deferredEvents == null);
-        _collection = collection;
-        _collection._deferredEvents = this;
-      }
-
-      public void Dispose()
-      {
-        _collection._deferredEvents = null;
-        foreach (var args in this)
-          _collection.OnCollectionChanged(args);
-      }
-    }
+        #region Private Types
+        sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
+        {
+            readonly RangeObservableCollection<T> _collection;
+            public DeferredEventsCollection(RangeObservableCollection<T> collection)
+            {
+                Debug.Assert(collection != null);
+                Debug.Assert(collection._deferredEvents == null);
+                _collection = collection;
+                _collection._deferredEvents = this;
+            }
 
-    #endregion Private Types
+            public void Dispose()
+            {
+                _collection._deferredEvents = null;
+                foreach (var args in this)
+                    _collection.OnCollectionChanged(args);
+            }
+        }
 
-  }
+        #endregion Private Types
+
+    }
 
-  /// <remarks>
-  /// To be kept outside <see cref="ObservableCollection{T}"/>, since otherwise, a new instance will be created for each generic type used.
-  /// </remarks>
-  internal static class EventArgsCache
-  {
-    internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs("Count");
-    internal static readonly PropertyChangedEventArgs IndexerPropertyChanged = new PropertyChangedEventArgs("Item[]");
-    internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
-  }
-}
+    /// <remarks>
+    /// To be kept outside <see cref="ObservableCollection{T}"/>, since otherwise, a new instance will be created for each generic type used.
+    /// </remarks>
+#pragma warning disable SA1402 // File may only contain a single type
+    internal static class EventArgsCache
+#pragma warning restore SA1402 // File may only contain a single type
+    {
+        internal static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs("Count");
+        internal static readonly PropertyChangedEventArgs IndexerPropertyChanged = new PropertyChangedEventArgs("Item[]");
+        internal static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
+    }
+}

+ 100 - 89
PixiEditor/Models/DataHolders/WpfObservableRangeCollection.cs

@@ -9,96 +9,107 @@ using System.Windows.Data;
 
 namespace PixiEditor.Models.DataHolders
 {
-public class WpfObservableRangeCollection<T> : RangeObservableCollection<T>
-{
+    public class WpfObservableRangeCollection<T> : RangeObservableCollection<T>
+    {
         public bool SuppressNotify { get; set; } = false;
-  DeferredEventsCollection _deferredEvents;
-
-  public WpfObservableRangeCollection()
-  {
-  }
-
-  public WpfObservableRangeCollection(IEnumerable<T> collection) : base(collection)
-  {
-  }
-
-  public WpfObservableRangeCollection(List<T> list) : base(list)
-  {
-  }
-
-
-  /// <summary>
-  /// Raise CollectionChanged event to any listeners.
-  /// Properties/methods modifying this ObservableCollection will raise
-  /// a collection changed event through this virtual method.
-  /// </summary>
-  /// <remarks>
-  /// When overriding this method, either call its base implementation
-  /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
-  /// </remarks>
-  protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
-  {
+        DeferredEventsCollection _deferredEvents;
+
+        public WpfObservableRangeCollection()
+        {
+        }
+
+        public WpfObservableRangeCollection(IEnumerable<T> collection) : base(collection)
+        {
+        }
+
+        public WpfObservableRangeCollection(List<T> list) : base(list)
+        {
+        }
+
+
+        /// <summary>
+        /// Raise CollectionChanged event to any listeners.
+        /// Properties/methods modifying this ObservableCollection will raise
+        /// a collection changed event through this virtual method.
+        /// </summary>
+        /// <remarks>
+        /// When overriding this method, either call its base implementation
+        /// or call <see cref="BlockReentrancy"/> to guard against reentrant collection changes.
+        /// </remarks>
+        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
+        {
             if (SuppressNotify) return;
-    var _deferredEvents = (ICollection<NotifyCollectionChangedEventArgs>) typeof(RangeObservableCollection<T>)
-      .GetField("_deferredEvents", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
-    if (_deferredEvents != null)
-    {
-      _deferredEvents.Add(e);
-      return;
-    }
-
-    foreach (var handler in GetHandlers())
-      if (IsRange(e) && handler.Target is CollectionView cv)
-        cv.Refresh();
-      else
-        handler(this, e);
-  }
-
-  protected override IDisposable DeferEvents() => new DeferredEventsCollection(this);
-
-  bool IsRange(NotifyCollectionChangedEventArgs e) => e.NewItems?.Count > 1 || e.OldItems?.Count > 1;
-
-  IEnumerable<NotifyCollectionChangedEventHandler> GetHandlers()
-  {
-    var info = typeof(ObservableCollection<T>).GetField(nameof(CollectionChanged),
-      BindingFlags.Instance | BindingFlags.NonPublic);
-    var @event = (MulticastDelegate) info.GetValue(this);
-    return @event?.GetInvocationList()
-             .Cast<NotifyCollectionChangedEventHandler>()
-             .Distinct()
-           ?? Enumerable.Empty<NotifyCollectionChangedEventHandler>();
-  }
-
-  class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
-  {
-    private readonly WpfObservableRangeCollection<T> _collection;
-
-    public DeferredEventsCollection(WpfObservableRangeCollection<T> collection)
-    {
-      Debug.Assert(collection != null);
-      Debug.Assert(collection._deferredEvents == null);
-      _collection = collection;
-      _collection._deferredEvents = this;
-    }
-
-    public void Dispose()
-    {
-      _collection._deferredEvents = null;
-
-      var handlers = _collection
-        .GetHandlers()
-        .ToLookup(h => h.Target is CollectionView);
-
-      foreach (var handler in handlers[false])
-      foreach (var e in this)
-        handler(_collection, e);
-
-      foreach (var cv in handlers[true]
-                 .Select(h => h.Target)
-                 .Cast<CollectionView>()
-                 .Distinct())
-        cv.Refresh();
+#pragma warning disable SA1312 // Variable names should begin with lower-case letter
+            var _deferredEvents = (ICollection<NotifyCollectionChangedEventArgs>)typeof(RangeObservableCollection<T>)
+#pragma warning restore SA1312 // Variable names should begin with lower-case letter
+              .GetField("_deferredEvents", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this);
+            if (_deferredEvents != null)
+            {
+                _deferredEvents.Add(e);
+                return;
+            }
+
+            foreach (var handler in GetHandlers())
+            {
+                if (IsRange(e) && handler.Target is CollectionView cv)
+                    cv.Refresh();
+                else
+                    handler(this, e);
+            }
+        }
+
+        protected override IDisposable DeferEvents() => new DeferredEventsCollection(this);
+
+        bool IsRange(NotifyCollectionChangedEventArgs e) => e.NewItems?.Count > 1 || e.OldItems?.Count > 1;
+
+        IEnumerable<NotifyCollectionChangedEventHandler> GetHandlers()
+        {
+            var info = typeof(ObservableCollection<T>).GetField(
+                nameof(CollectionChanged),
+                BindingFlags.Instance | BindingFlags.NonPublic);
+            var @event = (MulticastDelegate)info.GetValue(this);
+            return @event?.GetInvocationList()
+                     .Cast<NotifyCollectionChangedEventHandler>()
+                     .Distinct()
+                   ?? Enumerable.Empty<NotifyCollectionChangedEventHandler>();
+        }
+
+        class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
+        {
+            private readonly WpfObservableRangeCollection<T> _collection;
+
+            public DeferredEventsCollection(WpfObservableRangeCollection<T> collection)
+            {
+                Debug.Assert(collection != null);
+                Debug.Assert(collection._deferredEvents == null);
+                _collection = collection;
+                _collection._deferredEvents = this;
+            }
+
+            public void Dispose()
+            {
+                _collection._deferredEvents = null;
+
+                var handlers = _collection
+                  .GetHandlers()
+                  .ToLookup(h => h.Target is CollectionView);
+
+                foreach (var handler in handlers[false])
+                {
+                    foreach (var e in this)
+                    {
+                        handler(_collection, e);
+                    }
+                }
+
+                foreach (var cv in handlers[true]
+                           .Select(h => h.Target)
+                           .Cast<CollectionView>()
+                           .Distinct())
+                {
+                    cv.Refresh();
+                }
+            }
+        }
     }
-  }
 }
-}

+ 1 - 17
PixiEditor/Models/Dialogs/ConfirmationDialog.cs

@@ -1,26 +1,10 @@
 using PixiEditor.Models.Enums;
 using PixiEditor.Views;
-using System;
 
 namespace PixiEditor.Models.Dialogs
 {
     public static class ConfirmationDialog
-    {
-        [Obsolete(message: "Use Show(message, title) instead.")]
-        public static ConfirmationType Show(string message)
-        {
-            ConfirmationPopup popup = new ConfirmationPopup
-            {
-                Body = message
-            };
-            if (popup.ShowDialog().GetValueOrDefault())
-            {
-                return popup.Result ? ConfirmationType.Yes : ConfirmationType.No;
-            }
-
-            return ConfirmationType.Canceled;
-        }
-
+    {
         public static ConfirmationType Show(string message, string title)
         {
             ConfirmationPopup popup = new ConfirmationPopup

+ 2 - 2
PixiEditor/Models/Dialogs/ResizeDocumentDialog.cs

@@ -27,7 +27,7 @@ namespace PixiEditor.Models.Dialogs
                 if (width != value)
                 {
                     width = value;
-                    RaisePropertyChanged("Width");
+                    RaisePropertyChanged(nameof(Width));
                 }
             }
         }
@@ -40,7 +40,7 @@ namespace PixiEditor.Models.Dialogs
                 if (height != value)
                 {
                     height = value;
-                    RaisePropertyChanged("Height");
+                    RaisePropertyChanged(nameof(Height));
                 }
             }
         }

+ 0 - 8
PixiEditor/Models/Enums/CapType.cs

@@ -1,8 +0,0 @@
-namespace PixiEditor.Models.Enums
-{
-    public enum CapType
-    {
-        Square,
-        Round
-    }
-}

+ 0 - 27
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -1,7 +1,6 @@
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers.Utils;
-using PixiEditor.Models.Position;
 using PixiEditor.Parser;
 using PixiEditor.Parser.Skia;
 using SkiaSharp;
@@ -129,32 +128,6 @@ namespace PixiEditor.Models.ImageManipulation
                 maxPreviewHeight);
         }
 
-        public static Dictionary<Guid, SKColor[]> GetPixelsForSelection(Layer[] layers, Coordinates[] selection)
-        {
-            Dictionary<Guid, SKColor[]> result = new();
-
-            foreach (Layer layer in layers)
-            {
-                SKColor[] pixels = new SKColor[selection.Length];
-
-                for (int j = 0; j < pixels.Length; j++)
-                {
-                    Coordinates position = layer.GetRelativePosition(selection[j]);
-                    if (position.X < 0 || position.X > layer.Width - 1 || position.Y < 0 ||
-                        position.Y > layer.Height - 1)
-                    {
-                        continue;
-                    }
-
-                    var cl = layer.GetPixel(position.X, position.Y);
-                    pixels[j] = cl;
-                }
-                result[layer.GuidValue] = pixels;
-            }
-
-            return result;
-        }
-
         public static SKColor BlendColors(SKColor bottomColor, SKColor topColor)
         {
             if ((topColor.Alpha < 255 && topColor.Alpha > 0))

+ 0 - 99
PixiEditor/Models/ImageManipulation/Morphology.cs

@@ -1,99 +0,0 @@
-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 x = kernelOffset; x < width - kernelOffset; x++)
-                {
-                    byte value = 0;
-
-                    // Apply dilation
-                    for (int ykernel = -kernelOffset; ykernel <= kernelOffset; ykernel++)
-                    {
-                        for (int xkernel = -kernelOffset; xkernel <= kernelOffset; xkernel++)
-                        {
-                            if (mask[xkernel + kernelOffset, ykernel + kernelOffset] == 1)
-                            {
-                                value = Math.Max(value, byteImg[x + xkernel, y + ykernel]);
-                            }
-                            else
-                            {
-                                continue;
-                            }
-                        }
-                    }
-
-                    // Write processed data into the second array
-                    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);
-
-            for (int y = 0; y < byteArray.GetLength(1); y++)
-            {
-                for (int x = 0; x < width; x++)
-                {
-                    if (byteArray[x, y] == 1)
-                    {
-                        output.Add(new Coordinates(x + offset.X, y + offset.Y));
-                    }
-                }
-            }
-
-            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 x = 0; x < dimensions.Item1 + margin; x++)
-                {
-                    Coordinates cords = new Coordinates(x + minX, y + minY);
-                    array[x + margin, y + margin] = points.Contains(cords) ? (byte)1 : (byte)0;
-                }
-            }
-
-            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);
-        }
-    }
-}

+ 0 - 31
PixiEditor/Models/ImageManipulation/Transform.cs

@@ -1,31 +0,0 @@
-using PixiEditor.Models.Position;
-
-namespace PixiEditor.Models.ImageManipulation
-{
-    public static class Transform
-    {
-        /// <summary>
-        ///     Returns translation between two coordinates.
-        /// </summary>
-        /// <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)
-        {
-            int translationX = to.X - from.X;
-            int translationY = to.Y - from.Y;
-            return new Coordinates(translationX, translationY);
-        }
-
-        public static Coordinates[] Translate(Coordinates[] points, Coordinates vector)
-        {
-            Coordinates[] translatedPoints = new Coordinates[points.Length];
-            for (int i = 0; i < translatedPoints.Length; i++)
-            {
-                translatedPoints[i] = new Coordinates(points[i].X + vector.X, points[i].Y + vector.Y);
-            }
-
-            return translatedPoints;
-        }
-    }
-}

+ 0 - 19
PixiEditor/Models/Position/MousePositionConverter.cs

@@ -1,19 +0,0 @@
-using System.Drawing;
-using System.Runtime.InteropServices;
-
-namespace PixiEditor.Models.Position
-{
-    public static class MousePositionConverter
-    {
-        public static Coordinates CurrentCoordinates { get; set; }
-
-        public static Point GetCursorPosition()
-        {
-            GetCursorPos(out Point point);
-            return point;
-        }
-
-        [DllImport("user32.dll")]
-        private static extern bool GetCursorPos(out Point point);
-    }
-}

+ 0 - 41
PixiEditor/ViewModels/MenuButtonViewModel.cs

@@ -1,41 +0,0 @@
-using System.Windows;
-using PixiEditor.Helpers;
-
-namespace PixiEditor.ViewModels
-{
-    internal class MenuButtonViewModel : ViewModelBase
-    {
-        private Visibility listViewVisibility;
-
-        public MenuButtonViewModel()
-        {
-            OpenListViewCommand = new RelayCommand(OpenListView);
-            CloseListViewCommand = new RelayCommand(CloseListView);
-            ListViewVisibility = Visibility.Hidden;
-        }
-
-        public RelayCommand OpenListViewCommand { get; set; }
-
-        public RelayCommand CloseListViewCommand { get; set; }
-
-        public Visibility ListViewVisibility
-        {
-            get => listViewVisibility;
-            set
-            {
-                listViewVisibility = value;
-                RaisePropertyChanged("ListViewVisibility");
-            }
-        }
-
-        private void OpenListView(object parameter)
-        {
-            ListViewVisibility = ListViewVisibility == Visibility.Hidden ? Visibility.Visible : Visibility.Hidden;
-        }
-
-        private void CloseListView(object parameter)
-        {
-            ListViewVisibility = Visibility.Hidden;
-        }
-    }
-}

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

@@ -101,7 +101,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 if (updateZipExists || updateExeExists)
                 {
                     ViewModelMain.Current.UpdateSubViewModel.UpdateReadyToInstall = true;
-                    var result = ConfirmationDialog.Show("Update is ready to install. Do you want to install it now?");
+                    var result = ConfirmationDialog.Show("Update is ready to be installed. Do you want to install it now?", "New update");
                     if (result == Models.Enums.ConfirmationType.Yes)
                     {
                         if (updateZipExists && File.Exists(updaterPath))

+ 1 - 1
PixiEditor/ViewModels/ViewModelMain.cs

@@ -367,7 +367,7 @@ namespace PixiEditor.ViewModels
             BitmapManager.ActiveDocument.CenterViewportTrigger.Execute(this, new Size(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
         }
 
-        private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
+        private void BitmapUtility_BitmapChanged(object sender, EventArgs e)
         {
             BitmapManager.ActiveDocument.ChangesSaved = false;
             if (ToolsSubViewModel.ActiveTool is BitmapOperationTool)

+ 3 - 3
PixiEditor/Views/UserControls/Layers/LayersManager.xaml.cs

@@ -84,7 +84,7 @@ namespace PixiEditor.Views.UserControls.Layers
             manager.CachedLayerTreeRoot = newRoot;
             return;
             //layer tree caching goes after than and disabled for now
-
+            /*
             if (manager.CachedLayerTreeRoot == null || newRoot == null)
             {
                 manager.CachedLayerTreeRoot = newRoot;
@@ -94,7 +94,7 @@ namespace PixiEditor.Views.UserControls.Layers
             if (object.ReferenceEquals(manager.CachedLayerTreeRoot, newRoot))
                 return;
 
-            UpdateCachedTree(manager.CachedLayerTreeRoot, newRoot);
+            UpdateCachedTree(manager.CachedLayerTreeRoot, newRoot);*/
         }
 
         private static void UpdateCachedTree(IList<IHasGuid> tree, IList<IHasGuid> newTree)
@@ -463,4 +463,4 @@ namespace PixiEditor.Views.UserControls.Layers
             SelectedItem = sender;
         }
     }
-}
+}

+ 0 - 57
PixiEditor/Views/UserControls/MenuButton.xaml

@@ -1,57 +0,0 @@
-<UserControl x:Class="PixiEditor.Views.MenuButton"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-             xmlns:local="clr-namespace:PixiEditor.Views"
-             xmlns:vm="clr-namespace:PixiEditor.ViewModels"
-             xmlns:helpers="clr-namespace:PixiEditor.Helpers"
-             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
-             mc:Ignorable="d"
-             d:DesignHeight="40" d:DesignWidth="80" x:Name="menuButton"
-             DataContext="{DynamicResource MenuButtonViewModel}">
-    <UserControl.Resources>
-        <vm:MenuButtonViewModel x:Key="MenuButtonViewModel" />
-        <Style TargetType="ListViewItem">
-            <Style.Resources>
-                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Transparent" />
-                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
-
-            </Style.Resources>
-        </Style>
-    </UserControl.Resources>
-
-    <StackPanel Name="MainStackPanel">
-        <Button Content="{Binding ElementName=menuButton,Path=Text}" Style="{StaticResource MenuButton}"
-                HorizontalAlignment="Left" Command="{Binding OpenListViewCommand}" />
-        <ListView Visibility="{Binding ListViewVisibility}" Style="{StaticResource MenuListViewStyle}">
-            <ListView.ItemContainerStyle>
-                <Style TargetType="{x:Type ListViewItem}">
-                    <Setter Property="Background" Value="Transparent" />
-                    <Setter Property="VerticalContentAlignment"
-                            Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
-                    <Setter Property="Template">
-                        <Setter.Value>
-                            <ControlTemplate TargetType="{x:Type ListViewItem}">
-                                <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}"
-                                        BorderThickness="{TemplateBinding BorderThickness}" Background="Transparent"
-                                        Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
-                                    <ContentPresenter
-                                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
-                                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
-                                </Border>
-                                <ControlTemplate.Triggers>
-                                    <Trigger Property="IsMouseOver" Value="True">
-                                        <Setter Property="BorderThickness" Value="1" />
-                                        <Setter Property="BorderBrush" Value="Transparent" />
-                                    </Trigger>
-                                </ControlTemplate.Triggers>
-                            </ControlTemplate>
-                        </Setter.Value>
-                    </Setter>
-                </Style>
-            </ListView.ItemContainerStyle>
-            <ContentPresenter Content="{Binding Item, ElementName=menuButton}" />
-        </ListView>
-    </StackPanel>
-</UserControl>

+ 0 - 46
PixiEditor/Views/UserControls/MenuButton.xaml.cs

@@ -1,46 +0,0 @@
-using PixiEditor.ViewModels;
-using System.Windows;
-using System.Windows.Controls;
-
-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);
-        }
-    }
-}

+ 0 - 16
PixiEditor/Views/UserControls/Rotatebox.xaml

@@ -1,16 +0,0 @@
-<UserControl x:Class="PixiEditor.Views.Rotatebox"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:local="clr-namespace:PixiEditor.Views"
-             mc:Ignorable="d" 
-             d:DesignHeight="100" d:DesignWidth="160" x:Name="uc">
-    <StackPanel Orientation="Vertical" RenderTransformOrigin="0.5, 0.5">
-    <Image Name="knob" Source="../Images/AnchorDot.png" RenderTransformOrigin="0.5,0.5" Width="20" Height="20"/>
-        <Border Width="120" Height="60" BorderThickness="0.3" BorderBrush="DeepSkyBlue" CornerRadius="1"/>
-        <StackPanel.RenderTransform>
-            <RotateTransform Angle="{Binding Path=Angle, ElementName=uc}"/>
-        </StackPanel.RenderTransform>
-    </StackPanel>
-</UserControl>

+ 0 - 71
PixiEditor/Views/UserControls/Rotatebox.xaml.cs

@@ -1,71 +0,0 @@
-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
-    {
-        private double _height = 0, _width = 0;
-        private float _offset = 90;
-
-        public Rotatebox()
-        {
-            InitializeComponent();
-            MouseLeftButtonDown += OnMouseLeftButtonDown;
-            MouseUp += OnMouseUp;
-            MouseMove += OnMouseMove;
-        }
-
-        // Using a DependencyProperty backing store for Angle.
-        public static readonly DependencyProperty AngleProperty =
-            DependencyProperty.Register("Angle", typeof(double), typeof(Rotatebox), new UIPropertyMetadata(0.0));
-
-        public double Angle
-        {
-            get { return (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)
-                {
-                    Angle += 180;
-                }
-            }
-        }
-    }
-}

+ 0 - 1
PixiEditor/Views/UserControls/SizeInput.xaml

@@ -6,7 +6,6 @@
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              xmlns:behaviors="clr-namespace:PixiEditor.Helpers.Behaviours"
              xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
-             xmlns:validators="clr-namespace:PixiEditor.Helpers.Validators"
              mc:Ignorable="d" Foreground="White" Focusable="True"
              d:DesignHeight="30" d:DesignWidth="160" Name="uc">
 

+ 5 - 5
PixiEditorTests/ModelsTests/ControllersTests/MockedSinglePixelPenTool.cs

@@ -1,10 +1,9 @@
-using System.Collections.Generic;
-using System.Windows.Media;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using SkiaSharp;
+using System;
+using System.Collections.Generic;
 
 namespace PixiEditorTests.ModelsTests.ControllersTests
 {
@@ -15,8 +14,9 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement,
             SKColor color)
         {
+            if (recordedMouseMovement == null || activeLayer == null)
+                throw new ArgumentException("Parameter is null");
             activeLayer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(recordedMouseMovement[0].ToSKPoint(), color);
-
         }
     }
 }

+ 40 - 32
PixiEditorTests/ModelsTests/ControllersTests/UndoManagerTests.cs

@@ -1,5 +1,4 @@
 using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Undo;
 using Xunit;
 
@@ -20,7 +19,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestSetRoot()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
             Assert.Equal(this, undoManager.MainRoot);
         }
 
@@ -28,9 +27,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestAddToUndoStack()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("ExampleProperty", ExampleProperty, ExampleProperty));
+            using var change = new Change("ExampleProperty", ExampleProperty, ExampleProperty);
+            undoManager.AddUndoChange(change);
             Assert.True(undoManager.UndoStack.Count == 1);
             Assert.True((int)undoManager.UndoStack.Peek().OldValue == ExampleProperty);
         }
@@ -39,9 +39,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatUndoAddsToRedoStack()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("ExampleProperty", ExampleProperty, ExampleProperty));
+            using var change = new Change("ExampleProperty", ExampleProperty, ExampleProperty);
+            undoManager.AddUndoChange(change);
             undoManager.Undo();
             Assert.True(undoManager.RedoStack.Count == 1);
         }
@@ -50,9 +51,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestUndo()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("ExampleProperty", ExampleProperty, 55));
+            using var change = new Change("ExampleProperty", ExampleProperty, 55);
+            undoManager.AddUndoChange(change);
             ExampleProperty = 55;
             undoManager.Undo();
             Assert.True((int)undoManager.RedoStack.Peek().OldValue == ExampleProperty);
@@ -62,9 +64,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatRedoAddsToUndoStack()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("ExampleProperty", ExampleProperty, ExampleProperty));
+            using var change = new Change("ExampleProperty", ExampleProperty, ExampleProperty);
+            undoManager.AddUndoChange(change);
             undoManager.Undo();
             undoManager.Redo();
             Assert.True(undoManager.UndoStack.Count == 1);
@@ -74,10 +77,11 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestRedo()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
             ExampleProperty = 55;
-            undoManager.AddUndoChange(new Change("ExampleProperty", 1, ExampleProperty));
+            using var change = new Change("ExampleProperty", 1, ExampleProperty);
+            undoManager.AddUndoChange(change);
             undoManager.Undo();
             undoManager.Redo();
             Assert.True((int)undoManager.UndoStack.Peek().NewValue == ExampleProperty);
@@ -87,12 +91,13 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatUndoManagerUndoAndRedoWithCustomRootCorrectly()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
             TestPropertyClass testProp = new TestPropertyClass();
             int newVal = 5;
             testProp.IntProperty = newVal;
-            undoManager.AddUndoChange(new Change("IntProperty", 0, newVal, root: testProp));
+            using var change = new Change("IntProperty", 0, newVal, root: testProp);
+            undoManager.AddUndoChange(change);
             Assert.Equal(newVal, testProp.IntProperty);
 
             undoManager.Undo();
@@ -108,16 +113,15 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatMixedProcessOfUndoAndRedoWorks()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
             int newVal = 5;
-
-            undoManager.AddUndoChange(
-                new Change(
+            using var change = new Change(
                     "ExampleProperty",
                     ReverseProcess,
                     new object[] { ExampleProperty },
-                    newVal));
+                    newVal);
+            undoManager.AddUndoChange(change);
 
             ExampleProperty = newVal;
 
@@ -136,14 +140,15 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatProcessBasedUndoAndRedoWorks()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
             int newVal = 5;
-            undoManager.AddUndoChange(new Change(
+            using var change = new Change(
                 ReverseProcess,
                 new object[] { ExampleProperty },
                 ReverseProcess,
-                new object[] { newVal }));
+                new object[] { newVal });
+            undoManager.AddUndoChange(change);
 
             ExampleProperty = newVal;
 
@@ -162,11 +167,11 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatNestedPropertyUndoWorks()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
             int newVal = 5;
-
-            undoManager.AddUndoChange(new Change("TestPropClass.IntProperty", TestPropClass.IntProperty, newVal));
+            using var change = new Change("TestPropClass.IntProperty", TestPropClass.IntProperty, newVal);
+            undoManager.AddUndoChange(change);
 
             TestPropClass.IntProperty = newVal;
 
@@ -185,9 +190,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatFindRootProcessWorks()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("IntProperty", 0, 5, FindRootProcess, null));
+            using var change1 = new Change("IntProperty", 0, 5, FindRootProcess, null);
+            undoManager.AddUndoChange(change1);
 
             Change change = undoManager.UndoStack.Peek();
 
@@ -198,9 +204,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatUndoForFindRootProcessWorks()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("IntProperty", 0, 5, FindRootProcess, null));
+            using var change = new Change("IntProperty", 0, 5, FindRootProcess, null);
+            undoManager.AddUndoChange(change);
 
             TestPropClass.IntProperty = 5;
 
@@ -213,9 +220,10 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatUndoAndRedoForFindRootProcessWorks()
         {
             PrepareUndoManagerForTest();
-            UndoManager undoManager = new UndoManager(this);
+            using UndoManager undoManager = new UndoManager(this);
 
-            undoManager.AddUndoChange(new Change("IntProperty", 0, 5, FindRootProcess, null));
+            using var change = new Change("IntProperty", 0, 5, FindRootProcess, null);
+            undoManager.AddUndoChange(change);
 
             TestPropClass.IntProperty = 5;
 
@@ -244,4 +252,4 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             TestPropClass = new TestPropertyClass { IntProperty = 0 };
         }
     }
-}
+}

+ 1 - 24
PixiEditorTests/ModelsTests/DataHoldersTests/BitmapPixelChangesTests.cs

@@ -1,5 +1,4 @@
-using PixiEditor.Exceptions;
-using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Position;
 using SkiaSharp;
 using Xunit;
@@ -32,27 +31,5 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             Assert.Equal(SKColors.Red, output.ChangedPixels[new Coordinates(0, 0)]);
             Assert.Equal(SKColors.Lime, output.ChangedPixels[new Coordinates(1, 0)]);
         }
-
-        [Fact]
-        public void TestThatFromArraysThrowsError()
-        {
-            Assert.Throws<ArrayLengthMismatchException>(
-                () => BitmapPixelChanges.FromArrays(new[] { new Coordinates(0, 0) }, new[] { SKColors.Red, SKColors.Lime }));
-        }
-
-        [Fact]
-        public void TestThatFormArraysWorks()
-        {
-            Coordinates[] coordinatesArray = { new Coordinates(0, 0), new Coordinates(2, 3), new Coordinates(5, 5) };
-            SKColor[] colorsArray = { SKColors.Red, SKColors.Lime, SKColors.Blue };
-            BitmapPixelChanges result = BitmapPixelChanges.FromArrays(coordinatesArray, colorsArray);
-            for (int i = 0; i < coordinatesArray.Length; i++)
-            {
-                Coordinates cords = coordinatesArray[i];
-                Assert.Equal(colorsArray[i], result.ChangedPixels[cords]);
-            }
-
-            Assert.False(result.WasBuiltAsSingleColored);
-        }
     }
 }

+ 12 - 13
PixiEditorTests/ModelsTests/DataHoldersTests/DocumentLayersTests.cs

@@ -1,6 +1,5 @@
-using System;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.ViewModels.SubViewModels.Main;
+using PixiEditor.Models.DataHolders;
+using System;
 using Xunit;
 
 namespace PixiEditorTests.ModelsTests.DataHoldersTests
@@ -11,7 +10,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatToggleLayerDoesNotToggleLastLayer()
         {
-            Document doc = new (5, 5);
+            using Document doc = new(5, 5);
             doc.AddNewLayer("layer");
             bool isActive = doc.Layers[^1].IsActive;
             doc.ToggleLayer(0);
@@ -21,7 +20,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatToggleLayerTogglesLayer()
         {
-            Document doc = new (5, 5);
+            using Document doc = new(5, 5);
             doc.AddNewLayer("layer");
             doc.AddNewLayer("layer 1");
             doc.Layers[0].IsActive = true;
@@ -35,7 +34,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatToggleLayerDoesNothingOnNonExistingIndex()
         {
-            Document document = new Document(5, 5);
+            using Document document = new Document(5, 5);
             document.AddNewLayer("test");
             document.ToggleLayer(1);
             document.ToggleLayer(-1);
@@ -48,7 +47,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [InlineData(1, 1)]
         public void TestThatSelectLayersRangeSelectsRange(int startIndex, int endIndex)
         {
-            Document document = new Document(5, 5);
+            using Document document = new Document(5, 5);
 
             document.AddNewLayer("1");
             document.AddNewLayer("2");
@@ -62,7 +61,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             {
                 Assert.Equal(
                     i >= Math.Min(startIndex, endIndex)
-                    && i <= Math.Max(startIndex, endIndex), 
+                    && i <= Math.Max(startIndex, endIndex),
                     document.Layers[i].IsActive);
             }
         }
@@ -73,7 +72,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [InlineData(2)]
         public void TestThatDeselectAllExceptDeselectsAllExceptLayer(int index)
         {
-            Document document = new Document(5, 5);
+            using Document document = new Document(5, 5);
 
             document.AddNewLayer("1");
             document.AddNewLayer("2");
@@ -94,7 +93,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatUpdateLayersColorMakesOnlyOneLayerMainColorAndOtherSecondary()
         {
-            Document document = new Document(1, 1);
+            using Document document = new Document(1, 1);
 
             document.AddNewLayer("1");
             document.AddNewLayer("2");
@@ -114,7 +113,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatUpdateLayersColorMakesLayerMainColorAndRestNonActiveReturnsTransparent()
         {
-            Document document = new Document(1, 1);
+            using Document document = new Document(1, 1);
 
             document.AddNewLayer("1");
             document.AddNewLayer("2");
@@ -134,7 +133,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatSetNextSelectedLayerAsActiveSelectsFirstAvailableLayer()
         {
-            Document document = new Document(1, 1);
+            using Document document = new Document(1, 1);
 
             document.AddNewLayer("1");
             document.AddNewLayer("2");
@@ -151,4 +150,4 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
             Assert.Equal(document.Layers[0].GuidValue, document.ActiveLayerGuid);
         }
     }
-}
+}

+ 16 - 15
PixiEditorTests/ModelsTests/DataHoldersTests/LayerStructureTests.cs

@@ -10,7 +10,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatAddNewGroupAddsNewGroup()
         {
-            Document doc = new Document(1, 1);
+            using Document doc = new Document(1, 1);
             doc.Layers.Add(new("_testLayer", 1, 1));
             var testLayer = doc.Layers[^1];
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
@@ -23,7 +23,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatAddNewGroupAddsNewGroupAsASubgroup()
         {
-            Document doc = new Document(1, 1);
+            using Document doc = new Document(1, 1);
             doc.Layers.Add(new("_testLayer", 1, 1));
             var testLayer = doc.Layers[^1];
             doc.LayerStructure.AddNewGroup("test", testLayer.GuidValue);
@@ -40,7 +40,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatMoveGroupMovesSwapsLayerPlacesWithOtherGroup()
         {
-            Document doc = new Document(1, 1);
+            using Document doc = new Document(1, 1);
             doc.Layers.Add(new Layer("_testLayer", 1, 1));
             doc.Layers.Add(new Layer("_testLayer1", 1, 1));
             var testLayer = doc.Layers[0];
@@ -60,7 +60,8 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatIsChildOfDetectsNestedGroupCorrectly()
         {
-            LayerStructure ls = new LayerStructure(new Document(1, 1));
+            using var doc = new Document(1, 1);
+            LayerStructure ls = new LayerStructure(doc);
             Layer testLayer = new Layer("tst", 1, 1);
             ls.Groups.Add(new GuidStructureItem("group 1", testLayer.GuidValue));
             ls.Groups[0].Subgroups.Add(new GuidStructureItem("group 1 nested", testLayer.GuidValue));
@@ -72,7 +73,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatIsChildOfDetectsNestedLayersCorrectly()
         {
-            var doc = new Document(1, 1);
+            using var doc = new Document(1, 1);
             doc.Layers.Add(new Layer("tst", 1, 1));
             Guid testLayerGuid = doc.Layers[0].GuidValue;
             LayerStructure ls = new LayerStructure(doc);
@@ -86,7 +87,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatGroupContainsOnlyLayerDetectsOnlySingleLayerCorrectly()
         {
-            var doc = new Document(1, 1);
+            using var doc = new Document(1, 1);
             doc.Layers.Add(new Layer("layer", 1, 1));
             var guid = doc.Layers[0].GuidValue;
             doc.LayerStructure.AddNewGroup("layer group", guid);
@@ -96,7 +97,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatGroupContainsOnlyLayerDetectsOnlySingleLayerThatIsNested()
         {
-            var doc = new Document(1, 1);
+            using var doc = new Document(1, 1);
             doc.Layers.Add(new Layer("layer", 1, 1));
             var guid = doc.Layers[0].GuidValue;
             doc.LayerStructure.AddNewGroup("layer group", guid);
@@ -108,7 +109,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatCloneReturnsSameLayerStructure()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new("Test", 1, 1));
             doc.Layers.Add(new("Test2", 1, 1));
             LayerStructure structure = new(doc);
@@ -124,7 +125,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatGetGroupByGuidReturnsNullForNonExistingGroup()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new("Test", 1, 1));
 
             Assert.Null(doc.LayerStructure.GetGroupByGuid(null));
@@ -134,7 +135,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatGetGroupByGuidReturnsGroupCorrectly()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new("Test", 1, 1));
             var group = doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
 
@@ -144,7 +145,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatPreMoveReassignBoundsMakesNestedGroupEmptyAndRemovesItAndParent()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new("Test", 1, 1));
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
@@ -157,7 +158,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatPostMoveReassignBoundsAssignsNewLayerToGroup()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new("Test", 1, 1));
             doc.LayerStructure.AddNewGroup("Test group", doc.Layers[0].GuidValue);
             var group1 = doc.LayerStructure.AddNewGroup("Test group nested", doc.Layers[0].GuidValue);
@@ -180,7 +181,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatAssignParentAssignsParent()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new Layer("Test", 1, 1));
 
             var firstLayer = doc.Layers[0];
@@ -200,7 +201,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatAssignParentDeAssignsParentOnNull()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new Layer("Test", 1, 1));
 
             var firstLayer = doc.Layers[0];
@@ -221,7 +222,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatGetGroupLayersReturnsAllLayersInGroup()
         {
-            Document doc = new(1, 1);
+            using Document doc = new(1, 1);
             doc.Layers.Add(new Layer("Test", 1, 1));
             doc.Layers.Add(new Layer("Test 1", 1, 1));
             doc.Layers.Add(new Layer("Test 2", 1, 1));

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

@@ -13,7 +13,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatSetSelectionNewSetsCorrectSelection()
         {
-            Selection selection = new Selection(Array.Empty<Coordinates>(), new (10, 10));
+            Selection selection = new Selection(Array.Empty<Coordinates>(), new(10, 10));
             Coordinates[] points = { new Coordinates(0, 0), new Coordinates(1, 1) };
 
             selection.SetSelection(points, SelectionType.New);
@@ -62,7 +62,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [Fact]
         public void TestThatUndoWorks()
         {
-            Document document = new Document(10, 10);
+            using Document document = new Document(10, 10);
 
             IEnumerable<Coordinates> oldSelection = new List<Coordinates>(document.ActiveSelection.SelectedPoints);
 

+ 2 - 0
PixiEditorTests/ModelsTests/DataHoldersTests/SurfaceTests.cs

@@ -4,7 +4,9 @@ using Xunit;
 
 namespace PixiEditorTests.ModelsTests.DataHoldersTests
 {
+#pragma warning disable CA1001 // Types that own disposable fields should be disposable
     public class SurfaceTests
+#pragma warning restore CA1001 // Types that own disposable fields should be disposable
     {
         SKColor redColor = new SKColor(254, 2, 3);
         SKColor greenColor = new SKColor(6, 224, 3);

+ 0 - 39
PixiEditorTests/ModelsTests/ImageManipulationTests/TransformTests.cs

@@ -1,39 +0,0 @@
-using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.Position;
-using Xunit;
-
-namespace PixiEditorTests.ModelsTests.ImageManipulationTests
-{
-    public class TransformTests
-    {
-        [Theory]
-        [InlineData(0, 0, 1, 1, 1, 1)]
-        [InlineData(1, 1, 0, 0, -1, -1)]
-        [InlineData(5, 5, 4, 6, -1, 1)]
-        [InlineData(-15, -15, -16, -16, -1, -1)]
-        [InlineData(150, 150, 1150, 1150, 1000, 1000)]
-        public void TestGetTranslation(int x1, int y1, int x2, int y2, int expectedX, int expectedY)
-        {
-            Coordinates translation = Transform.GetTranslation(new Coordinates(x1, y1), new Coordinates(x2, y2));
-            Assert.Equal(new Coordinates(expectedX, expectedY), translation);
-        }
-
-        [Theory]
-        [InlineData(0, 0)]
-        [InlineData(1, 1)]
-        [InlineData(5, 2)]
-        [InlineData(50, 150)]
-        [InlineData(-5, -52)]
-        public void TestTranslate(int vectorX, int vectorY)
-        {
-            Coordinates[] points = { new Coordinates(0, 0), new Coordinates(5, 5), new Coordinates(15, 2) };
-            Coordinates[] translatedCords = Transform.Translate(points, new Coordinates(vectorX, vectorY));
-
-            for (int i = 0; i < points.Length; i++)
-            {
-                Assert.Equal(points[i].X + vectorX, translatedCords[i].X);
-                Assert.Equal(points[i].Y + vectorY, translatedCords[i].Y);
-            }
-        }
-    }
-}

+ 5 - 1
PixiEditorTests/ModelsTests/LayersTests/LayersTestHelper.cs

@@ -7,6 +7,9 @@ namespace PixiEditorTests.ModelsTests.LayersTests
     {
         public static void LayersAreEqual(Layer expected, Layer actual)
         {
+            Assert.NotNull(actual);
+            Assert.NotNull(expected);
+#pragma warning disable CA1062 // Validate arguments of public methods
             Assert.Equal(expected.Name, actual.Name);
             Assert.Equal(expected.Offset, actual.Offset);
             Assert.Equal(expected.Width, actual.Width);
@@ -17,6 +20,7 @@ namespace PixiEditorTests.ModelsTests.LayersTests
             Assert.Equal(expected.IsVisible, actual.IsVisible);
             Assert.Equal(expected.IsRenaming, actual.IsRenaming);
             Assert.Equal(expected.ConvertBitmapToBytes(), actual.ConvertBitmapToBytes());
+#pragma warning restore CA1062 // Validate arguments of public methods
         }
     }
-}
+}

+ 3 - 2
PixiEditorTests/ModelsTests/PositionTests/CoordinatesTests.cs

@@ -1,4 +1,5 @@
 using PixiEditor.Models.Position;
+using System.Globalization;
 using Xunit;
 
 namespace PixiEditorTests.ModelsTests.PositionTests
@@ -10,7 +11,7 @@ namespace PixiEditorTests.ModelsTests.PositionTests
         {
             Coordinates cords = new Coordinates(5, 5);
 
-            Assert.Equal("5, 5", cords.ToString());
+            Assert.Equal("5, 5", cords.ToString(CultureInfo.InvariantCulture));
         }
 
         [Fact]
@@ -22,4 +23,4 @@ namespace PixiEditorTests.ModelsTests.PositionTests
             Assert.True(cords != cords2);
         }
     }
-}
+}

+ 16 - 17
PixiEditorTests/ModelsTests/UndoTests/StorageBasedChangeTests.cs

@@ -5,7 +5,6 @@ using PixiEditor.Models.Undo;
 using PixiEditorTests.ModelsTests.LayersTests;
 using SkiaSharp;
 using System;
-using System.Collections.ObjectModel;
 using System.IO;
 using Xunit;
 
@@ -23,11 +22,11 @@ namespace PixiEditorTests.ModelsTests.UndoTests
             }
         }
 
-        public Document GenerateTestDocument()
+        public static Document GenerateTestDocument()
         {
-            Document testDocument = new Document(10, 10);
-            Surface testBitmap = new Surface(10, 10);
-            Surface testBitmap2 = new Surface(5, 8);
+            using Document testDocument = new Document(10, 10);
+            using Surface testBitmap = new Surface(10, 10);
+            using Surface testBitmap2 = new Surface(5, 8);
             testBitmap.SetSRGBPixel(0, 0, SKColors.Black);
             testBitmap2.SetSRGBPixel(4, 4, SKColors.Blue);
             Random random = new Random();
@@ -42,9 +41,9 @@ namespace PixiEditorTests.ModelsTests.UndoTests
         [Fact]
         public void TestThatConstructorGeneratesUndoLayersProperly()
         {
-            Document testDocument = GenerateTestDocument();
+            using Document testDocument = GenerateTestDocument();
 
-            StorageBasedChange change = new StorageBasedChange(testDocument, testDocument.Layers, UndoStoreLocation);
+            using StorageBasedChange change = new StorageBasedChange(testDocument, testDocument.Layers, UndoStoreLocation);
 
             Assert.Equal(testDocument.Layers.Count, change.StoredLayers.Length);
 
@@ -69,9 +68,9 @@ namespace PixiEditorTests.ModelsTests.UndoTests
         [Fact]
         public void TestThatSaveLayersOnDeviceSavesLayers()
         {
-            Document document = GenerateTestDocument();
+            using Document document = GenerateTestDocument();
 
-            StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
+            using StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
 
             foreach (var layer in change.StoredLayers)
             {
@@ -83,9 +82,9 @@ namespace PixiEditorTests.ModelsTests.UndoTests
         [Fact]
         public void TestThatLoadLayersFromDeviceLoadsLayers()
         {
-            Document document = GenerateTestDocument();
+            using Document document = GenerateTestDocument();
 
-            StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
+            using StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
 
             Layer[] layers = change.LoadLayersFromDevice();
 
@@ -101,9 +100,9 @@ namespace PixiEditorTests.ModelsTests.UndoTests
         [Fact]
         public void TestThatUndoInvokesLoadFromDeviceAndExecutesProcess()
         {
-            Document document = GenerateTestDocument();
+            using Document document = GenerateTestDocument();
 
-            StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
+            using StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
             bool undoInvoked = false;
 
             Action<Layer[], UndoLayer[]> testUndoProcess = (layers, data) =>
@@ -120,7 +119,7 @@ namespace PixiEditorTests.ModelsTests.UndoTests
             Action<object[]> testRedoProcess = parameters => { };
 
             Change undoChange = change.ToChange(testUndoProcess, testRedoProcess, null);
-            UndoManager manager = new UndoManager(this);
+            using UndoManager manager = new UndoManager(this);
 
             manager.AddUndoChange(undoChange);
             manager.Undo();
@@ -131,9 +130,9 @@ namespace PixiEditorTests.ModelsTests.UndoTests
         [Fact]
         public void TestThatRedoInvokesSaveToDeviceAndExecutesProcess()
         {
-            Document document = GenerateTestDocument();
+            using Document document = GenerateTestDocument();
 
-            StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
+            using StorageBasedChange change = new StorageBasedChange(document, document.Layers, UndoStoreLocation);
             bool redoInvoked = false;
 
             Action<Layer[], UndoLayer[]> testUndoProcess = (layers, data) => { };
@@ -152,7 +151,7 @@ namespace PixiEditorTests.ModelsTests.UndoTests
             };
 
             Change undoChange = change.ToChange(testUndoProcess, testRedoProcess, new object[] { 2 });
-            UndoManager manager = new UndoManager(this);
+            using UndoManager manager = new UndoManager(this);
 
             manager.AddUndoChange(undoChange);
             manager.Undo();