瀏覽代碼

fix merge conflict

Equbuxu 4 年之前
父節點
當前提交
6c200972e5
共有 27 個文件被更改,包括 442 次插入149 次删除
  1. 61 0
      PixiEditor/Helpers/DependencyInjectionHelper.cs
  2. 1 1
      PixiEditor/Models/Tools/ShapeTool.cs
  3. 2 0
      PixiEditor/Models/Tools/Tool.cs
  4. 71 0
      PixiEditor/Models/Tools/ToolBuilder.cs
  5. 2 3
      PixiEditor/Models/Tools/ToolSettings/Toolbars/BrightnessToolToolbar.cs
  6. 2 2
      PixiEditor/Models/Tools/Tools/BrightnessTool.cs
  7. 4 2
      PixiEditor/Models/Tools/Tools/EraserTool.cs
  8. 8 5
      PixiEditor/Models/Tools/Tools/FloodFill.cs
  9. 4 4
      PixiEditor/Models/Tools/Tools/LineTool.cs
  10. 31 25
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  11. 10 5
      PixiEditor/Models/Tools/Tools/MoveViewportTool.cs
  12. 8 6
      PixiEditor/Models/Tools/Tools/PenTool.cs
  13. 10 4
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  14. 4 1
      PixiEditor/Models/Tools/Tools/ZoomTool.cs
  15. 6 3
      PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs
  16. 1 2
      PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs
  17. 33 7
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  18. 39 8
      PixiEditor/ViewModels/ViewModelMain.cs
  19. 16 6
      PixiEditor/Views/MainWindow.xaml.cs
  20. 58 0
      PixiEditorTests/Helpers/ViewModelHelper.cs
  21. 1 1
      PixiEditorTests/HelpersTests/ConvertersTests/DoubleToIntConverterTest.cs
  22. 41 0
      PixiEditorTests/HelpersTests/DependencyInjectionTests.cs
  23. 2 1
      PixiEditorTests/ModelsTests/DataHoldersTests/DocumentTests.cs
  24. 2 1
      PixiEditorTests/ModelsTests/ToolsTests/PenToolTests.cs
  25. 0 24
      PixiEditorTests/TestHelpers.cs
  26. 19 35
      PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs
  27. 6 3
      README.md

+ 61 - 0
PixiEditor/Helpers/DependencyInjectionHelper.cs

@@ -0,0 +1,61 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace PixiEditor.Helpers
+{
+    public static class DependencyInjectionHelper
+    {
+        public static T Inject<T>(this IServiceProvider provider)
+            => (T)Inject(provider, typeof(T));
+
+#nullable enable
+
+        public static object Inject(this IServiceProvider provider, Type type)
+        {
+            ConstructorInfo constructor = FindConstructorOrDefault(provider, type);
+
+            List<object?> parameters = new List<object?>();
+
+            foreach (Type argumentType in constructor.GetParameters().Select(x => x.ParameterType))
+            {
+                parameters.Add(provider.GetRequiredService(argumentType));
+            }
+
+            return constructor.Invoke(parameters.ToArray());
+        }
+
+#nullable disable
+
+        private static ConstructorInfo FindConstructorOrDefault(IServiceProvider provider, Type type)
+        {
+            ConstructorInfo foundConstructor = default;
+
+            foreach (ConstructorInfo info in type.GetConstructors())
+            {
+                if (HasParameters(provider, info.GetParameters()))
+                {
+                    foundConstructor = info;
+                    break;
+                }
+            }
+
+            return foundConstructor;
+        }
+
+        private static bool HasParameters(IServiceProvider provider, IEnumerable<ParameterInfo> parameters)
+        {
+            foreach (ParameterInfo parameter in parameters)
+            {
+                if (provider.GetService(parameter.ParameterType) is null)
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}

+ 1 - 1
PixiEditor/Models/Tools/ShapeTool.cs

@@ -58,7 +58,7 @@ namespace PixiEditor.Models.Tools
         // TODO: Add cache for lines 31, 32 (hopefully it would speed up calculation)
         // TODO: Add cache for lines 31, 32 (hopefully it would speed up calculation)
         public abstract override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color);
         public abstract override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color);
 
 
-        protected IEnumerable<Coordinates> GetThickShape(IEnumerable<Coordinates> shape, int thickness)
+        protected static IEnumerable<Coordinates> GetThickShape(IEnumerable<Coordinates> shape, int thickness)
         {
         {
             List<Coordinates> output = new List<Coordinates>();
             List<Coordinates> output = new List<Coordinates>();
             foreach (Coordinates item in shape)
             foreach (Coordinates item in shape)

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

@@ -49,6 +49,8 @@ namespace PixiEditor.Models.Tools
 
 
         public Toolbar Toolbar { get; set; } = new EmptyToolbar();
         public Toolbar Toolbar { get; set; } = new EmptyToolbar();
 
 
+        public IServiceProvider Services { get; set; }
+
         public bool CanStartOutsideCanvas { get; set; } = false;
         public bool CanStartOutsideCanvas { get; set; } = false;
 
 
         public virtual void OnMouseDown(MouseEventArgs e)
         public virtual void OnMouseDown(MouseEventArgs e)

+ 71 - 0
PixiEditor/Models/Tools/ToolBuilder.cs

@@ -0,0 +1,71 @@
+using PixiEditor.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace PixiEditor.Models.Tools
+{
+    /// <summary>
+    /// Handles Depdency Injection of tools
+    /// </summary>
+    public class ToolBuilder
+    {
+        private readonly IServiceProvider services;
+
+        private readonly List<Type> toBuild = new List<Type>();
+
+        public ToolBuilder(IServiceProvider services)
+        {
+            this.services = services;
+        }
+
+        /// <summary>
+        /// Constructs a new tool of type <typeparamref name="T"/> and injects all services of <paramref name="services"/>
+        /// </summary>
+        public static T BuildTool<T>(IServiceProvider services)
+            where T : Tool
+            => (T)BuildTool(typeof(T), services);
+
+        /// <summary>
+        /// Constructs a new tool of type <paramref name="type"/> and injects all services of <paramref name="services"/>
+        /// </summary>
+        public static Tool BuildTool(Type type, IServiceProvider services)
+        {
+            Tool tool = (Tool)services.Inject(type);
+
+            return tool;
+        }
+
+        /// <summary>
+        /// Adds a new tool of type <typeparamref name="T"/> to the building chain.
+        /// </summary>
+        public ToolBuilder Add<T>()
+            where T : Tool
+            => Add(typeof(T));
+
+        /// <summary>
+        /// Adds a new tool of type <paramref name="type"/> to the building chain.
+        /// </summary>
+        public ToolBuilder Add(Type type)
+        {
+            toBuild.Add(type);
+
+            return this;
+        }
+
+        /// <summary>
+        /// Builds all added tools.
+        /// </summary>
+        public IEnumerable<Tool> Build()
+        {
+            List<Tool> tools = new List<Tool>();
+
+            foreach (Type type in toBuild)
+            {
+                tools.Add(BuildTool(type, services));
+            }
+
+            return tools;
+        }
+    }
+}

+ 2 - 3
PixiEditor/Models/Tools/ToolSettings/Toolbars/BrightnessToolToolbar.cs

@@ -1,5 +1,4 @@
-using System;
-using PixiEditor.Models.Enums;
+using PixiEditor.Models.Enums;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 
 
 namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
 namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
@@ -9,7 +8,7 @@ namespace PixiEditor.Models.Tools.ToolSettings.Toolbars
         public BrightnessToolToolbar(float initialValue)
         public BrightnessToolToolbar(float initialValue)
         {
         {
             Settings.Add(new FloatSetting("CorrectionFactor", initialValue, "Strength:", 0f, 100f));
             Settings.Add(new FloatSetting("CorrectionFactor", initialValue, "Strength:", 0f, 100f));
-            Settings.Add(new DropdownSetting("BrightnessMode", Enum.GetNames(typeof(BrightnessMode)), "Mode"));
+            Settings.Add(new EnumSetting<BrightnessMode>("BrightnessMode", "Mode"));
         }
         }
     }
     }
 }
 }

+ 2 - 2
PixiEditor/Models/Tools/Tools/BrightnessTool.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Windows.Controls;
 using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media;
+using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Colors;
 using PixiEditor.Models.Colors;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
@@ -53,8 +54,7 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             int toolSize = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
             int toolSize = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
             float correctionFactor = Toolbar.GetSetting<FloatSetting>("CorrectionFactor").Value;
             float correctionFactor = Toolbar.GetSetting<FloatSetting>("CorrectionFactor").Value;
-            Enum.TryParse((Toolbar.GetSetting<DropdownSetting>("BrightnessMode")?.Value as ComboBoxItem)?.Content as string, out BrightnessMode mode);
-            Mode = mode;
+            Mode = Toolbar.GetEnumSetting<BrightnessMode>("BrightnessMode").Value;
 
 
             LayerChange[] layersChanges = new LayerChange[1];
             LayerChange[] layersChanges = new LayerChange[1];
             if (Keyboard.IsKeyDown(Key.LeftCtrl))
             if (Keyboard.IsKeyDown(Key.LeftCtrl))

+ 4 - 2
PixiEditor/Models/Tools/Tools/EraserTool.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows.Media;
 using System.Windows.Media;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
@@ -10,13 +11,14 @@ namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class EraserTool : BitmapOperationTool
     public class EraserTool : BitmapOperationTool
     {
     {
-        private readonly PenTool pen = new PenTool();
+        private readonly PenTool pen;
 
 
-        public EraserTool()
+        public EraserTool(BitmapManager bitmapManager)
         {
         {
             ActionDisplay = "Draw to remove color from a pixel.";
             ActionDisplay = "Draw to remove color from a pixel.";
             Tooltip = "Erasers color from pixel. (E)";
             Tooltip = "Erasers color from pixel. (E)";
             Toolbar = new BasicToolbar();
             Toolbar = new BasicToolbar();
+            pen = new PenTool(bitmapManager);
         }
         }
 
 
         public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color)
         public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color)

+ 8 - 5
PixiEditor/Models/Tools/Tools/FloodFill.cs

@@ -1,18 +1,21 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows.Media;
 using System.Windows.Media;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
-using PixiEditor.ViewModels;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class FloodFill : BitmapOperationTool
     public class FloodFill : BitmapOperationTool
     {
     {
-        public FloodFill()
+        private BitmapManager BitmapManager { get; }
+
+        public FloodFill(BitmapManager bitmapManager)
         {
         {
             ActionDisplay = "Press on a area to fill it.";
             ActionDisplay = "Press on a area to fill it.";
             Tooltip = "Fills area with color. (G)";
             Tooltip = "Fills area with color. (G)";
+            BitmapManager = bitmapManager;
         }
         }
 
 
         public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color)
         public override LayerChange[] Use(Layer layer, List<Coordinates> coordinates, Color color)
@@ -20,12 +23,12 @@ namespace PixiEditor.Models.Tools.Tools
             return Only(ForestFire(layer, coordinates[0], color), layer);
             return Only(ForestFire(layer, coordinates[0], color), layer);
         }
         }
 
 
-        private BitmapPixelChanges ForestFire(Layer layer, Coordinates startingCoords, Color newColor)
+        public BitmapPixelChanges ForestFire(Layer layer, Coordinates startingCoords, Color newColor)
         {
         {
             List<Coordinates> changedCoords = new List<Coordinates>();
             List<Coordinates> changedCoords = new List<Coordinates>();
 
 
-            int width = ViewModelMain.Current.BitmapManager.ActiveDocument.Width;
-            int height = ViewModelMain.Current.BitmapManager.ActiveDocument.Height;
+            int width = BitmapManager.ActiveDocument.Width;
+            int height = BitmapManager.ActiveDocument.Height;
 
 
             var visited = new bool[width, height];
             var visited = new bool[width, height];
 
 

+ 4 - 4
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -58,13 +58,13 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
         public IEnumerable<Coordinates> CreateLine(Coordinates start, Coordinates end, int thickness, CapType startCap, CapType endCap)
         {
         {
-            return CreateLine(new() { end, start }, thickness, startCap, endCap);
+            return CreateLine(new List<Coordinates>() { end, start }, thickness, startCap, endCap);
         }
         }
 
 
-        private IEnumerable<Coordinates> CreateLine(List<Coordinates> coordinates, int thickness, CapType startCap, CapType endCap)
+        private IEnumerable<Coordinates> CreateLine(IEnumerable<Coordinates> coordinates, int thickness, CapType startCap, CapType endCap)
         {
         {
-            Coordinates startingCoordinates = coordinates[^1];
-            Coordinates latestCoordinates = coordinates[0];
+            Coordinates startingCoordinates = coordinates.Last();
+            Coordinates latestCoordinates = coordinates.First();
             if (thickness == 1)
             if (thickness == 1)
             {
             {
                 return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X, latestCoordinates.Y);
                 return BresenhamLine(startingCoordinates.X, startingCoordinates.Y, latestCoordinates.X, latestCoordinates.Y);

+ 31 - 25
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -29,7 +29,7 @@ namespace PixiEditor.Models.Tools.Tools
         private Coordinates[] startSelection;
         private Coordinates[] startSelection;
         private bool updateViewModelSelection = true;
         private bool updateViewModelSelection = true;
 
 
-        public MoveTool()
+        public MoveTool(BitmapManager bitmapManager)
         {
         {
             ActionDisplay = "Hold mouse to move selected pixels. Hold Ctrl to move all layers.";
             ActionDisplay = "Hold mouse to move selected pixels. Hold Ctrl to move all layers.";
             Tooltip = "Moves selected pixels (V). Hold Ctrl to move all layers.";
             Tooltip = "Moves selected pixels (V). Hold Ctrl to move all layers.";
@@ -37,10 +37,14 @@ namespace PixiEditor.Models.Tools.Tools
             HideHighlight = true;
             HideHighlight = true;
             RequiresPreviewLayer = true;
             RequiresPreviewLayer = true;
             UseDefaultUndoMethod = true;
             UseDefaultUndoMethod = true;
+
+            BitmapManager = bitmapManager;
         }
         }
 
 
         public bool MoveAll { get; set; } = false;
         public bool MoveAll { get; set; } = false;
 
 
+        private BitmapManager BitmapManager { get; }
+
         public override void OnKeyDown(KeyEventArgs e)
         public override void OnKeyDown(KeyEventArgs e)
         {
         {
             if (e.Key == Key.LeftCtrl)
             if (e.Key == Key.LeftCtrl)
@@ -59,31 +63,33 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public override void AfterAddedUndo(UndoManager undoManager)
         public override void AfterAddedUndo(UndoManager undoManager)
         {
         {
-            if (currentSelection != null && currentSelection.Length > 0)
+            if (currentSelection == null || currentSelection.Length == 0)
             {
             {
-                Change changes = undoManager.UndoStack.Peek();
+                return;
+            }
 
 
-                // Inject to default undo system change custom changes made by this tool
-                foreach (var item in startPixelColors)
-                {
-                    BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(startSelection, item.Value);
-                    BitmapPixelChanges afterMovePixels = BitmapPixelChanges.FromArrays(currentSelection, endPixelColors[item.Key]);
-                    Guid layerGuid = item.Key;
-                    var oldValue = (LayerChange[])changes.OldValue;
+            Change changes = undoManager.UndoStack.Peek();
 
 
-                    if (oldValue.Any(x => x.LayerGuid == layerGuid))
-                    {
-                        var layer = oldValue.First(x => x.LayerGuid == layerGuid);
-                        layer.PixelChanges.ChangedPixels.AddRangeOverride(afterMovePixels.ChangedPixels);
-                        layer.PixelChanges.ChangedPixels
-                            .AddRangeOverride(beforeMovePixels.ChangedPixels);
+            // Inject to default undo system change custom changes made by this tool
+            foreach (var item in startPixelColors)
+            {
+                BitmapPixelChanges beforeMovePixels = BitmapPixelChanges.FromArrays(startSelection, item.Value);
+                BitmapPixelChanges afterMovePixels = BitmapPixelChanges.FromArrays(currentSelection, endPixelColors[item.Key]);
+                Guid layerGuid = item.Key;
+                var oldValue = (LayerChange[])changes.OldValue;
 
 
-                        ((LayerChange[])changes.NewValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels
-                            .AddRangeNewOnly(BitmapPixelChanges
-                                .FromSingleColoredArray(startSelection, System.Windows.Media.Colors.Transparent)
-                                .ChangedPixels);
-                    }
-                }
+                if (oldValue.Any(x => x.LayerGuid == layerGuid))
+                {
+                    var layer = oldValue.First(x => x.LayerGuid == layerGuid);
+                    layer.PixelChanges.ChangedPixels.AddRangeOverride(afterMovePixels.ChangedPixels);
+                    layer.PixelChanges.ChangedPixels
+                        .AddRangeOverride(beforeMovePixels.ChangedPixels);
+
+                    ((LayerChange[])changes.NewValue).First(x => x.LayerGuid == layerGuid).PixelChanges.ChangedPixels
+                        .AddRangeNewOnly(BitmapPixelChanges
+                            .FromSingleColoredArray(startSelection, System.Windows.Media.Colors.Transparent)
+                            .ChangedPixels);
+                }
             }
             }
         }
         }
 
 
@@ -93,7 +99,7 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             if (currentSelection != null && currentSelection.Length == 0)
             if (currentSelection != null && currentSelection.Length == 0)
             {
             {
-                ViewModelMain.Current.BitmapManager.ActiveDocument.UndoManager.AddUndoChange(new Change(
+                BitmapManager.ActiveDocument.UndoManager.AddUndoChange(new Change(
                     ApplyOffsets,
                     ApplyOffsets,
                     new object[] { startingOffsets },
                     new object[] { startingOffsets },
                     ApplyOffsets,
                     ApplyOffsets,
@@ -107,7 +113,7 @@ namespace PixiEditor.Models.Tools.Tools
             ResetSelectionValues(startPos);
             ResetSelectionValues(startPos);
 
 
             // Move offset if no selection
             // Move offset if no selection
-            var doc = ViewModelMain.Current.BitmapManager.ActiveDocument;
+            Document doc = BitmapManager.ActiveDocument;
             Selection selection = doc.ActiveSelection;
             Selection selection = doc.ActiveSelection;
             if (selection != null && selection.SelectedPoints.Count > 0)
             if (selection != null && selection.SelectedPoints.Count > 0)
             {
             {
@@ -232,7 +238,7 @@ namespace PixiEditor.Models.Tools.Tools
             Guid layerGuid = layer.LayerGuid;
             Guid layerGuid = layer.LayerGuid;
             if (!clearedPixels.ContainsKey(layerGuid) || clearedPixels[layerGuid] == false)
             if (!clearedPixels.ContainsKey(layerGuid) || clearedPixels[layerGuid] == false)
             {
             {
-                ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.First(x => x == layer)
+                BitmapManager.ActiveDocument.Layers.First(x => x == layer)
                     .SetPixels(BitmapPixelChanges.FromSingleColoredArray(selection, System.Windows.Media.Colors.Transparent));
                     .SetPixels(BitmapPixelChanges.FromSingleColoredArray(selection, System.Windows.Media.Colors.Transparent));
 
 
                 clearedPixels[layerGuid] = true;
                 clearedPixels[layerGuid] = true;

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

@@ -1,25 +1,30 @@
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
-using System.Collections.Generic;
-using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class MoveViewportTool : ReadonlyTool
     public class MoveViewportTool : ReadonlyTool
-    {
-        public MoveViewportTool()
+    {
+        private BitmapManager BitmapManager { get; }
+
+        private ToolsViewModel ToolsViewModel { get; }
+
+        public MoveViewportTool(BitmapManager bitmapManager, ToolsViewModel toolsViewModel)
         {
         {
             HideHighlight = true;
             HideHighlight = true;
             Cursor = Cursors.SizeAll;
             Cursor = Cursors.SizeAll;
             ActionDisplay = "Click and move to pan viewport.";
             ActionDisplay = "Click and move to pan viewport.";
             Tooltip = "Move viewport. (H)";
             Tooltip = "Move viewport. (H)";
+
+            BitmapManager = bitmapManager;
+            ToolsViewModel = toolsViewModel;
         }
         }
 
 
         public override void OnMouseUp(MouseEventArgs e)
         public override void OnMouseUp(MouseEventArgs e)
         {
         {
             if (e.MiddleButton == MouseButtonState.Pressed)
             if (e.MiddleButton == MouseButtonState.Pressed)
             {
             {
-                ViewModelMain.Current.ToolsSubViewModel.SetActiveTool(ViewModelMain.Current.ToolsSubViewModel.LastActionTool);
+                ToolsViewModel.SetActiveTool(ToolsViewModel.LastActionTool);
             }
             }
         }
         }
 
 

+ 8 - 6
PixiEditor/Models/Tools/Tools/PenTool.cs

@@ -1,11 +1,11 @@
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using PixiEditor.ViewModels;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
@@ -18,12 +18,14 @@ namespace PixiEditor.Models.Tools.Tools
     {
     {
         private readonly SizeSetting toolSizeSetting;
         private readonly SizeSetting toolSizeSetting;
         private readonly BoolSetting pixelPerfectSetting;
         private readonly BoolSetting pixelPerfectSetting;
-        private readonly LineTool lineTool;
         private readonly List<Coordinates> confirmedPixels = new List<Coordinates>();
         private readonly List<Coordinates> confirmedPixels = new List<Coordinates>();
+        private readonly LineTool lineTool;
         private Coordinates[] lastChangedPixels = new Coordinates[3];
         private Coordinates[] lastChangedPixels = new Coordinates[3];
         private byte changedPixelsindex = 0;
         private byte changedPixelsindex = 0;
 
 
-        public PenTool()
+        private BitmapManager BitmapManager { get; }
+
+        public PenTool(BitmapManager bitmapManager)
         {
         {
             Cursor = Cursors.Pen;
             Cursor = Cursors.Pen;
             ActionDisplay = "Click and move to draw.";
             ActionDisplay = "Click and move to draw.";
@@ -32,9 +34,9 @@ namespace PixiEditor.Models.Tools.Tools
             toolSizeSetting = Toolbar.GetSetting<SizeSetting>("ToolSize");
             toolSizeSetting = Toolbar.GetSetting<SizeSetting>("ToolSize");
             pixelPerfectSetting = Toolbar.GetSetting<BoolSetting>("PixelPerfectEnabled");
             pixelPerfectSetting = Toolbar.GetSetting<BoolSetting>("PixelPerfectEnabled");
             pixelPerfectSetting.ValueChanged += PixelPerfectSettingValueChanged;
             pixelPerfectSetting.ValueChanged += PixelPerfectSettingValueChanged;
-            RequiresPreviewLayer = pixelPerfectSetting.Value;
-            lineTool = new LineTool();
             ClearPreviewLayerOnEachIteration = false;
             ClearPreviewLayerOnEachIteration = false;
+            BitmapManager = bitmapManager;
+            lineTool = new LineTool();
         }
         }
 
 
         public override void OnRecordingLeftMouseDown(MouseEventArgs e)
         public override void OnRecordingLeftMouseDown(MouseEventArgs e)
@@ -54,7 +56,7 @@ namespace PixiEditor.Models.Tools.Tools
                 color,
                 color,
                 toolSizeSetting.Value,
                 toolSizeSetting.Value,
                 pixelPerfectSetting.Value,
                 pixelPerfectSetting.Value,
-                ViewModelMain.Current.BitmapManager.ActiveDocument.PreviewLayer);
+                BitmapManager.ActiveDocument.PreviewLayer);
             return Only(pixels, layer);
             return Only(pixels, layer);
         }
         }
 
 

+ 10 - 4
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -19,17 +19,23 @@ namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class SelectTool : ReadonlyTool
     public class SelectTool : ReadonlyTool
     {
     {
-        private readonly RectangleTool rectangleTool = new RectangleTool();
-        private readonly CircleTool circleTool = new CircleTool();
+        private readonly RectangleTool rectangleTool;
+        private readonly CircleTool circleTool;
         private IEnumerable<Coordinates> oldSelectedPoints;
         private IEnumerable<Coordinates> oldSelectedPoints;
 
 
         private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
         private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
 
 
-        public SelectTool()
+        private BitmapManager BitmapManager { get; }
+
+        public SelectTool(BitmapManager bitmapManager)
         {
         {
             ActionDisplay = "Click and move to select an area.";
             ActionDisplay = "Click and move to select an area.";
             Tooltip = "Selects area. (M)";
             Tooltip = "Selects area. (M)";
             Toolbar = new SelectToolToolbar();
             Toolbar = new SelectToolToolbar();
+            BitmapManager = bitmapManager;
+
+            rectangleTool = new RectangleTool();
+            circleTool = new CircleTool();
         }
         }
 
 
         public SelectionType SelectionType { get; set; } = SelectionType.Add;
         public SelectionType SelectionType { get; set; } = SelectionType.Add;
@@ -107,7 +113,7 @@ namespace PixiEditor.Models.Tools.Tools
                 throw new NotImplementedException($"Selection shape '{shape}' has not been implemented");
                 throw new NotImplementedException($"Selection shape '{shape}' has not been implemented");
             }
             }
 
 
-            ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selection, SelectionType);
+            BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selection, SelectionType);
         }
         }
     }
     }
 }
 }

+ 4 - 1
PixiEditor/Models/Tools/Tools/ZoomTool.cs

@@ -6,12 +6,15 @@ namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class ZoomTool : ReadonlyTool
     public class ZoomTool : ReadonlyTool
     {
     {
-        public ZoomTool()
+        private BitmapManager BitmapManager { get; }
+
+        public ZoomTool(BitmapManager bitmapManager)
         {
         {
             HideHighlight = true;
             HideHighlight = true;
             CanStartOutsideCanvas = true;
             CanStartOutsideCanvas = true;
             ActionDisplay = "Click and move to zoom. Click to zoom in, hold alt and click to zoom out.";
             ActionDisplay = "Click and move to zoom. Click to zoom in, hold alt and click to zoom out.";
             Tooltip = "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
             Tooltip = "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
+            BitmapManager = bitmapManager;
         }
         }
 
 
         public override void OnKeyDown(KeyEventArgs e)
         public override void OnKeyDown(KeyEventArgs e)

+ 6 - 3
PixiEditor/ViewModels/SubViewModels/Main/SelectionViewModel.cs

@@ -4,6 +4,7 @@ using PixiEditor.Helpers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
@@ -14,20 +15,22 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public RelayCommand SelectAllCommand { get; set; }
         public RelayCommand SelectAllCommand { get; set; }
 
 
+        private readonly SelectTool selectTool;
+
         public SelectionViewModel(ViewModelMain owner)
         public SelectionViewModel(ViewModelMain owner)
             : base(owner)
             : base(owner)
         {
         {
             DeselectCommand = new RelayCommand(Deselect, SelectionIsNotEmpty);
             DeselectCommand = new RelayCommand(Deselect, SelectionIsNotEmpty);
             SelectAllCommand = new RelayCommand(SelectAll, CanSelectAll);
             SelectAllCommand = new RelayCommand(SelectAll, CanSelectAll);
+
+            selectTool = new SelectTool(Owner.BitmapManager);
         }
         }
 
 
         public void SelectAll(object parameter)
         public void SelectAll(object parameter)
         {
         {
-            SelectTool select = new SelectTool();
-
             var oldSelection = new List<Coordinates>(Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints);
             var oldSelection = new List<Coordinates>(Owner.BitmapManager.ActiveDocument.ActiveSelection.SelectedPoints);
 
 
-            Owner.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(select.GetAllSelection(), SelectionType.New);
+            Owner.BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selectTool.GetAllSelection(), SelectionType.New);
             SelectionHelpers.AddSelectionUndoStep(Owner.BitmapManager.ActiveDocument, oldSelection, SelectionType.New);
             SelectionHelpers.AddSelectionUndoStep(Owner.BitmapManager.ActiveDocument, oldSelection, SelectionType.New);
         }
         }
 
 

+ 1 - 2
PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs

@@ -45,14 +45,13 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         public StylusViewModel(ViewModelMain owner)
         public StylusViewModel(ViewModelMain owner)
             : base(owner)
             : base(owner)
         {
         {
-            SetOwner(owner);
         }
         }
 
 
         public void SetOwner(ViewModelMain owner)
         public void SetOwner(ViewModelMain owner)
         {
         {
             if (Owner is not null)
             if (Owner is not null)
             {
             {
-                throw new System.Exception("StylusViewModel already has an owner");
+                throw new System.Exception($"{nameof(StylusViewModel)} already has an owner");
             }
             }
             else if (owner is null)
             else if (owner is null)
             {
             {

+ 33 - 7
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq;
+using System.Reflection;
 using System.Windows.Input;
 using System.Windows.Input;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
@@ -35,14 +36,20 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         {
         {
             SelectToolCommand = new RelayCommand(SetTool, Owner.DocumentIsNotNull);
             SelectToolCommand = new RelayCommand(SetTool, Owner.DocumentIsNotNull);
             ChangeToolSizeCommand = new RelayCommand(ChangeToolSize);
             ChangeToolSizeCommand = new RelayCommand(ChangeToolSize);
+        }
 
 
-            ToolSet = new ObservableCollection<Tool>
-            {
-                new MoveViewportTool(), new MoveTool(), new PenTool(), new SelectTool(), new FloodFill(), new LineTool(),
-                new CircleTool(), new RectangleTool(), new EraserTool(), new ColorPickerTool(), new BrightnessTool(),
-                new ZoomTool()
-            };
-            SetActiveTool(typeof(MoveViewportTool));
+        public void SetupTools(IServiceProvider services)
+        {
+            ToolBuilder builder = new ToolBuilder(services);
+
+            builder
+                .Add<MoveViewportTool>().Add<MoveTool>().Add<PenTool>().Add<SelectTool>().Add<FloodFill>()
+                .Add<LineTool>().Add<CircleTool>().Add<RectangleTool>().Add<EraserTool>().Add<ColorPickerTool>().Add<BrightnessTool>()
+                .Add<ZoomTool>();
+
+            ToolSet = new(builder.Build());
+
+            SetActiveTool<MoveViewportTool>();
         }
         }
 
 
         public void SetActiveTool<T>()
         public void SetActiveTool<T>()
@@ -82,6 +89,25 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             SetActiveTool(tool.GetType());
             SetActiveTool(tool.GetType());
         }
         }
 
 
+        private static T CreateTool<T>(IServiceProvider provider)
+            where T : new()
+        {
+            T tool = default;
+            Type toolType = typeof(T);
+
+            foreach (PropertyInfo info in toolType.GetProperties(BindingFlags.Public))
+            {
+                if (!info.CanWrite)
+                {
+                    continue;
+                }
+
+                info.SetValue(tool, provider.GetService(info.PropertyType));
+            }
+
+            return tool;
+        }
+
         private void ChangeToolSize(object parameter)
         private void ChangeToolSize(object parameter)
         {
         {
             int increment = (int)parameter;
             int increment = (int)parameter;

+ 39 - 8
PixiEditor/ViewModels/ViewModelMain.cs

@@ -30,6 +30,8 @@ namespace PixiEditor.ViewModels
 
 
         public static ViewModelMain Current { get; set; }
         public static ViewModelMain Current { get; set; }
 
 
+        public IServiceProvider Services { get; private set; }
+
         public Action CloseAction { get; set; }
         public Action CloseAction { get; set; }
 
 
         public event EventHandler OnStartupEvent;
         public event EventHandler OnStartupEvent;
@@ -118,15 +120,28 @@ namespace PixiEditor.ViewModels
 #endif
 #endif
         }
         }
 
 
-        public ViewModelMain(IServiceProvider services)
+        public ViewModelMain(IServiceCollection services)
         {
         {
             Current = this;
             Current = this;
 
 
+            ConfigureServices(services);
+            Setup(services.BuildServiceProvider());
+        }
+
+        public void ConfigureServices(IServiceCollection collection)
+        {
+            collection.AddSingleton(this);
+        }
+
+        public void Setup(IServiceProvider services)
+        {
+            Services = services;
+
             Preferences = services.GetRequiredService<IPreferences>();
             Preferences = services.GetRequiredService<IPreferences>();
 
 
             Preferences.Init();
             Preferences.Init();
 
 
-            BitmapManager = new BitmapManager();
+            BitmapManager = services.GetRequiredService<BitmapManager>();
             BitmapManager.BitmapOperations.BitmapChanged += BitmapUtility_BitmapChanged;
             BitmapManager.BitmapOperations.BitmapChanged += BitmapUtility_BitmapChanged;
             BitmapManager.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
             BitmapManager.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
             BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
             BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
@@ -138,7 +153,9 @@ namespace PixiEditor.ViewModels
             CloseWindowCommand = new RelayCommand(CloseWindow);
             CloseWindowCommand = new RelayCommand(CloseWindow);
 
 
             FileSubViewModel = new FileViewModel(this);
             FileSubViewModel = new FileViewModel(this);
-            ToolsSubViewModel = new ToolsViewModel(this);
+            ToolsSubViewModel = GetSubViewModel<ToolsViewModel>(services);
+            ToolsSubViewModel.SetupTools(services);
+
             IoSubViewModel = new IoViewModel(this);
             IoSubViewModel = new IoViewModel(this);
             LayersSubViewModel = new LayersViewModel(this);
             LayersSubViewModel = new LayersViewModel(this);
             ClipboardSubViewModel = new ClipboardViewModel(this);
             ClipboardSubViewModel = new ClipboardViewModel(this);
@@ -149,11 +166,8 @@ namespace PixiEditor.ViewModels
             DiscordViewModel = new DiscordViewModel(this, "764168193685979138");
             DiscordViewModel = new DiscordViewModel(this, "764168193685979138");
             UpdateSubViewModel = new UpdateViewModel(this);
             UpdateSubViewModel = new UpdateViewModel(this);
 
 
-            WindowSubViewModel = services.GetService<WindowViewModel>();
-            WindowSubViewModel?.SetOwner(this);
-
-            StylusSubViewModel = services.GetService<StylusViewModel>();
-            StylusSubViewModel?.SetOwner(this);
+            WindowSubViewModel = GetSubViewModel<WindowViewModel>(services, false);
+            StylusSubViewModel = GetSubViewModel<StylusViewModel>(services);
 
 
             AddDebugOnlyViewModels();
             AddDebugOnlyViewModels();
             AddReleaseOnlyViewModels();
             AddReleaseOnlyViewModels();
@@ -352,5 +366,22 @@ namespace PixiEditor.ViewModels
                 ColorsSubViewModel.AddSwatch(ColorsSubViewModel.PrimaryColor);
                 ColorsSubViewModel.AddSwatch(ColorsSubViewModel.PrimaryColor);
             }
             }
         }
         }
+
+        private T GetSubViewModel<T>(IServiceProvider services, bool isRequired = true)
+        {
+            T subViewModel = services.GetService<T>();
+
+            if (subViewModel is null && isRequired)
+            {
+                throw new InvalidOperationException($"No required view model for type '{typeof(T)}' has been registered.");
+            }
+
+            if (subViewModel is ISettableOwner<ViewModelMain> settable)
+            {
+                settable.SetOwner(this);
+            }
+
+            return subViewModel;
+        }
     }
     }
 }
 }

+ 16 - 6
PixiEditor/Views/MainWindow.xaml.cs

@@ -12,6 +12,7 @@ using PixiEditor.Views.Dialogs;
 using System.Windows.Media.Imaging;
 using System.Windows.Media.Imaging;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using System.Windows.Interop;
 using System.Windows.Interop;
+using PixiEditor.Models.Controllers;
 
 
 namespace PixiEditor
 namespace PixiEditor
 {
 {
@@ -22,7 +23,7 @@ namespace PixiEditor
     {
     {
         private static WriteableBitmap pixiEditorLogo;
         private static WriteableBitmap pixiEditorLogo;
 
 
-        private PreferencesSettings preferences;
+        private readonly PreferencesSettings preferences;
 
 
         public new ViewModelMain DataContext { get => (ViewModelMain)base.DataContext; set => base.DataContext = value; }
         public new ViewModelMain DataContext { get => (ViewModelMain)base.DataContext; set => base.DataContext = value; }
 
 
@@ -30,12 +31,9 @@ namespace PixiEditor
         {
         {
             preferences = new PreferencesSettings();
             preferences = new PreferencesSettings();
 
 
-            IServiceCollection services = new ServiceCollection()
-                .AddSingleton<IPreferences>(preferences)
-                .AddSingleton<StylusViewModel>()
-                .AddSingleton<WindowViewModel>();
+            IServiceCollection services = ConfigureServices(new ServiceCollection());
 
 
-            DataContext = new ViewModelMain(services.BuildServiceProvider());
+            DataContext = new ViewModelMain(services);
 
 
             InitializeComponent();
             InitializeComponent();
 
 
@@ -80,6 +78,18 @@ namespace PixiEditor
             Application.Current.Windows.OfType<HelloTherePopup>().ToList().ForEach(x => { if (!x.IsClosing) x.Close(); });
             Application.Current.Windows.OfType<HelloTherePopup>().ToList().ForEach(x => { if (!x.IsClosing) x.Close(); });
         }
         }
 
 
+        private IServiceCollection ConfigureServices(IServiceCollection services)
+        {
+            services
+                .AddSingleton<IPreferences>(preferences)
+                .AddSingleton<StylusViewModel>()
+                .AddSingleton<WindowViewModel>()
+                .AddSingleton<BitmapManager>()
+                .AddSingleton<ToolsViewModel>();
+
+            return services;
+        }
+
         private void BitmapManager_DocumentChanged(object sender, Models.Events.DocumentChangedEventArgs e)
         private void BitmapManager_DocumentChanged(object sender, Models.Events.DocumentChangedEventArgs e)
         {
         {
             if (preferences.GetPreference("ImagePreviewInTaskbar", false))
             if (preferences.GetPreference("ImagePreviewInTaskbar", false))

+ 58 - 0
PixiEditorTests/Helpers/ViewModelHelper.cs

@@ -0,0 +1,58 @@
+using Microsoft.Extensions.DependencyInjection;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Tools;
+using PixiEditor.Models.UserPreferences;
+using PixiEditor.ViewModels;
+using PixiEditor.ViewModels.SubViewModels.Main;
+using PixiEditorTests.Mocks;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditorTests.HelpersTests
+{
+    public static class ViewModelHelper
+    {
+        public static IServiceCollection GetViewModelMainCollection()
+        {
+            return new ServiceCollection()
+                .AddScoped<IPreferences, PreferenceSettingsMock>()
+                .AddSingleton<BitmapManager>();
+        }
+
+        public static ViewModelMain MockedViewModelMain()
+        {
+            IServiceCollection provider = MockedServiceCollection();
+
+            return new ViewModelMain(provider);
+        }
+
+        public static IServiceCollection MockedServiceCollection()
+        {
+            return new ServiceCollection()
+                .AddSingleton<IPreferences>(new Mocks.PreferenceSettingsMock())
+                .AddSingleton<StylusViewModel>()
+                .AddSingleton<BitmapManager>()
+                .AddSingleton<ToolsViewModel>();
+        }
+
+        public static T BuildMockedTool<T>(bool requireViewModelMain = false)
+            where T : Tool
+        {
+            IServiceProvider services;
+
+            if (requireViewModelMain)
+            {
+                services = MockedViewModelMain().Services;
+            }
+            else
+            {
+                services = MockedServiceCollection().BuildServiceProvider();
+            }
+
+            return ToolBuilder.BuildTool<T>(services);
+        }
+    }
+}

+ 1 - 1
PixiEditorTests/HelpersTests/ConvertersTests/DoubleToIntConverterTest.cs

@@ -2,7 +2,7 @@
 using PixiEditor.Helpers.Converters;
 using PixiEditor.Helpers.Converters;
 using Xunit;
 using Xunit;
 
 
-namespace PixiEditorTests.HelpersTests.ConvertersTests
+namespace PixiEditorTests.Helpers
 {
 {
     public class DoubleToIntConverterTest
     public class DoubleToIntConverterTest
     {
     {

+ 41 - 0
PixiEditorTests/HelpersTests/DependencyInjectionTests.cs

@@ -0,0 +1,41 @@
+using Microsoft.Extensions.DependencyInjection;
+using PixiEditor.Helpers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace PixiEditorTests.HelpersTests
+{
+    [Collection("Application collection")]
+    public class DependencyInjectionTests
+    {
+        private class TestService
+        {
+        }
+
+        private class TestInjectable
+        {
+            public TestService TestService { get; }
+
+            public TestInjectable(TestService service)
+            {
+                TestService = service;
+            }
+        }
+
+        [Fact]
+        public void TestThatInjectingWorks()
+        {
+            IServiceProvider provider = new ServiceCollection()
+                .AddSingleton<TestService>()
+                .BuildServiceProvider();
+
+            TestInjectable injectable = provider.Inject<TestInjectable>();
+
+            Assert.NotNull(injectable.TestService);
+        }
+    }
+}

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

@@ -6,6 +6,7 @@ using PixiEditor.Models.Enums;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
+using PixiEditorTests.HelpersTests;
 using Xunit;
 using Xunit;
 
 
 namespace PixiEditorTests.ModelsTests.DataHoldersTests
 namespace PixiEditorTests.ModelsTests.DataHoldersTests
@@ -301,7 +302,7 @@ namespace PixiEditorTests.ModelsTests.DataHoldersTests
         [StaFact]
         [StaFact]
         public void TestThatDocumentGetsAddedToRecentlyOpenedList()
         public void TestThatDocumentGetsAddedToRecentlyOpenedList()
         {
         {
-            ViewModelMain viewModel = TestHelpers.MockedViewModelMain();
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             Document document = new Document(1, 1)
             Document document = new Document(1, 1)
             {
             {

+ 2 - 1
PixiEditorTests/ModelsTests/ToolsTests/PenToolTests.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
+using PixiEditorTests.HelpersTests;
 using Xunit;
 using Xunit;
 
 
 namespace PixiEditorTests.ModelsTests.ToolsTests
 namespace PixiEditorTests.ModelsTests.ToolsTests
@@ -10,7 +11,7 @@ namespace PixiEditorTests.ModelsTests.ToolsTests
         [StaFact]
         [StaFact]
         public void TestThatPixelPerfectPenReturnsShapeWithoutLShapePixels()
         public void TestThatPixelPerfectPenReturnsShapeWithoutLShapePixels()
         {
         {
-            PenTool pen = new PenTool();
+            PenTool pen = ViewModelHelper.BuildMockedTool<PenTool>();
 
 
             Coordinates start = new Coordinates(0, 0);
             Coordinates start = new Coordinates(0, 0);
             Coordinates end = new Coordinates(0, 0);
             Coordinates end = new Coordinates(0, 0);

+ 0 - 24
PixiEditorTests/TestHelpers.cs

@@ -1,24 +0,0 @@
-using System;
-using Microsoft.Extensions.DependencyInjection;
-using PixiEditor.Models.UserPreferences;
-using PixiEditor.ViewModels;
-
-namespace PixiEditorTests
-{
-    public static class TestHelpers
-    {
-        public static ViewModelMain MockedViewModelMain()
-        {
-            IServiceProvider provider = MockedServiceProvider();
-
-            return new ViewModelMain(provider);
-        }
-
-        public static IServiceProvider MockedServiceProvider()
-        {
-            return new ServiceCollection()
-                .AddSingleton<IPreferences>(new Mocks.PreferenceSettingsMock())
-                .BuildServiceProvider();
-        }
-    }
-}

+ 19 - 35
PixiEditorTests/ViewModelsTests/ViewModelMainTests.cs

@@ -1,16 +1,11 @@
-using System;
-using System.IO;
-using System.Windows.Input;
-using System.Windows.Media;
-using Microsoft.Extensions.DependencyInjection;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.IO;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
-using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
+using PixiEditorTests.HelpersTests;
+using System.IO;
+using System.Windows.Input;
+using System.Windows.Media;
 using Xunit;
 using Xunit;
 
 
 namespace PixiEditorTests.ViewModelsTests
 namespace PixiEditorTests.ViewModelsTests
@@ -18,21 +13,10 @@ namespace PixiEditorTests.ViewModelsTests
     [Collection("Application collection")]
     [Collection("Application collection")]
     public class ViewModelMainTests
     public class ViewModelMainTests
     {
     {
-        private static IServiceProvider services;
-
-        public static IServiceProvider Services { get => services; set => services = value; }
-
-        public ViewModelMainTests()
-        {
-            Services = new ServiceCollection()
-                .AddSingleton<IPreferences>(new Mocks.PreferenceSettingsMock())
-                .BuildServiceProvider();
-        }
-
         [StaFact]
         [StaFact]
         public void TestThatConstructorSetsUpControllersCorrectly()
         public void TestThatConstructorSetsUpControllersCorrectly()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             Assert.NotNull(viewModel.ChangesController);
             Assert.NotNull(viewModel.ChangesController);
             Assert.NotNull(viewModel.ShortcutController);
             Assert.NotNull(viewModel.ShortcutController);
@@ -44,7 +28,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatSwapColorsCommandSwapsColors()
         public void TestThatSwapColorsCommandSwapsColors()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             viewModel.ColorsSubViewModel.PrimaryColor = Colors.Black;
             viewModel.ColorsSubViewModel.PrimaryColor = Colors.Black;
             viewModel.ColorsSubViewModel.SecondaryColor = Colors.White;
             viewModel.ColorsSubViewModel.SecondaryColor = Colors.White;
@@ -58,7 +42,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatNewDocumentCreatesNewDocumentWithBaseLayer()
         public void TestThatNewDocumentCreatesNewDocumentWithBaseLayer()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             viewModel.FileSubViewModel.NewDocument(5, 5);
             viewModel.FileSubViewModel.NewDocument(5, 5);
 
 
@@ -69,7 +53,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatMouseMoveCommandUpdatesCurrentCoordinates()
         public void TestThatMouseMoveCommandUpdatesCurrentCoordinates()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
             viewModel.BitmapManager.ActiveDocument = new Document(10, 10);
             viewModel.BitmapManager.ActiveDocument = new Document(10, 10);
 
 
             Assert.Equal(new Coordinates(0, 0), MousePositionConverter.CurrentCoordinates);
             Assert.Equal(new Coordinates(0, 0), MousePositionConverter.CurrentCoordinates);
@@ -85,7 +69,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatSelectToolCommandSelectsNewTool()
         public void TestThatSelectToolCommandSelectsNewTool()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             Assert.Equal(typeof(MoveViewportTool), viewModel.BitmapManager.SelectedTool.GetType());
             Assert.Equal(typeof(MoveViewportTool), viewModel.BitmapManager.SelectedTool.GetType());
 
 
@@ -97,7 +81,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatMouseUpCommandStopsRecordingMouseMovements()
         public void TestThatMouseUpCommandStopsRecordingMouseMovements()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             viewModel.BitmapManager.MouseController.StartRecordingMouseMovementChanges(true);
             viewModel.BitmapManager.MouseController.StartRecordingMouseMovementChanges(true);
 
 
@@ -111,7 +95,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatNewLayerCommandCreatesNewLayer()
         public void TestThatNewLayerCommandCreatesNewLayer()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
 
 
@@ -125,7 +109,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatSaveDocumentCommandSavesFile()
         public void TestThatSaveDocumentCommandSavesFile()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
             string fileName = "testFile.pixi";
             string fileName = "testFile.pixi";
 
 
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1)
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1)
@@ -143,7 +127,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatAddSwatchAddsNonDuplicateSwatch()
         public void TestThatAddSwatchAddsNonDuplicateSwatch()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
 
 
             viewModel.ColorsSubViewModel.AddSwatch(Colors.Green);
             viewModel.ColorsSubViewModel.AddSwatch(Colors.Green);
@@ -163,10 +147,10 @@ namespace PixiEditorTests.ViewModelsTests
         [InlineData(120, 150)]
         [InlineData(120, 150)]
         public void TestThatSelectAllCommandSelectsWholeDocument(int docWidth, int docHeight)
         public void TestThatSelectAllCommandSelectsWholeDocument(int docWidth, int docHeight)
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services)
-            {
-                BitmapManager = { ActiveDocument = new Document(docWidth, docHeight) }
-            };
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
+
+            viewModel.BitmapManager.ActiveDocument = new Document(docWidth, docHeight);
+
             viewModel.BitmapManager.ActiveDocument.AddNewLayer("layer");
             viewModel.BitmapManager.ActiveDocument.AddNewLayer("layer");
 
 
             viewModel.SelectionSubViewModel.SelectAllCommand.Execute(null);
             viewModel.SelectionSubViewModel.SelectAllCommand.Execute(null);
@@ -179,7 +163,7 @@ namespace PixiEditorTests.ViewModelsTests
         [StaFact]
         [StaFact]
         public void TestThatDocumentIsNotNullReturnsTrue()
         public void TestThatDocumentIsNotNullReturnsTrue()
         {
         {
-            ViewModelMain viewModel = new ViewModelMain(Services);
+            ViewModelMain viewModel = ViewModelHelper.MockedViewModelMain();
 
 
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
             viewModel.BitmapManager.ActiveDocument = new Document(1, 1);
 
 

+ 6 - 3
README.md

@@ -13,6 +13,8 @@
 [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/PixiEditor?label=%20r%2FPixiEditor&logoColor=%23e3002d)](https://reddit.com/r/PixiEditor)
 [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/PixiEditor?label=%20r%2FPixiEditor&logoColor=%23e3002d)](https://reddit.com/r/PixiEditor)
 [![contributions](https://img.shields.io/badge/contributions-open-brightgreen)](https://github.com/flabbet/PixiEditor/pulls)
 [![contributions](https://img.shields.io/badge/contributions-open-brightgreen)](https://github.com/flabbet/PixiEditor/pulls)
 
 
+### Check out our website [pixieditor.net](https://pixieditor.net)
+
 ## About PixiEditor
 ## About PixiEditor
 
 
 Want to create beautiful pixel arts for your games? PixiEditor can help you! Our goal is to create fully open-source, fast and feature rich pixel art creator. 
 Want to create beautiful pixel arts for your games? PixiEditor can help you! Our goal is to create fully open-source, fast and feature rich pixel art creator. 
@@ -43,9 +45,10 @@ PixiEditor started in 2018 and it's been actively developed since. We continuous
 
 
 Follow these instructions to get PixiEditor working on your machine.
 Follow these instructions to get PixiEditor working on your machine.
 
 
-1. Download the .exe file from [here](https://github.com/flabbet/PixiEditor/releases)
-2. Open installer
-3. Follow installer instructions
+1. Download the zipped installer from our official website
+2. Extract the installer from the archive
+3. Launch it
+4. Follow the steps in the installer to finish the installation
 
 
 
 
 ## Featured content
 ## Featured content