Browse Source

Added reference layer picking, improved Dependency Injection and moved tool stuff to ToolsViewModel

CPKreuz 3 years ago
parent
commit
55f3377963

+ 63 - 0
PixiEditor/Helpers/Extensions/ServiceCollectionHelpers.cs

@@ -0,0 +1,63 @@
+using Microsoft.Extensions.DependencyInjection;
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.Controllers.Shortcuts;
+using PixiEditor.Models.Services;
+using PixiEditor.Models.Tools;
+using PixiEditor.Models.Tools.Tools;
+using PixiEditor.Models.UserPreferences;
+using PixiEditor.ViewModels;
+using PixiEditor.ViewModels.SubViewModels.Main;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PixiEditor.Helpers.Extensions
+{
+    public static class ServiceCollectionHelpers
+    {
+        /// <summary>
+        /// Add's all the services required to fully run PixiEditor's MainWindow
+        /// </summary>
+        public static IServiceCollection AddPixiEditor(this IServiceCollection collection) => collection
+                .AddSingleton<ViewModelMain>()
+                .AddSingleton<IPreferences, PreferencesSettings>()
+                // View Models
+                .AddSingleton<StylusViewModel>()
+                .AddSingleton<WindowViewModel>()
+                .AddSingleton<ToolsViewModel>()
+                .AddSingleton<FileViewModel>()
+                .AddSingleton<UpdateViewModel>()
+                .AddSingleton<IoViewModel>()
+                .AddSingleton<LayersViewModel>()
+                .AddSingleton<ClipboardViewModel>()
+                .AddSingleton<UndoViewModel>()
+                .AddSingleton<SelectionViewModel>()
+                .AddSingleton<ViewportViewModel>()
+                .AddSingleton<ColorsViewModel>()
+                .AddSingleton<DocumentViewModel>()
+                .AddSingleton<MiscViewModel>()
+                .AddSingleton(x => new DiscordViewModel(x.GetService<ViewModelMain>(), "764168193685979138"))
+                .AddSingleton<DebugViewModel>()
+                // Controllers
+                .AddSingleton<ShortcutController>()
+                .AddSingleton<BitmapManager>()
+                // Tools
+                .AddSingleton<Tool, MoveViewportTool>()
+                .AddSingleton<Tool, MoveTool>()
+                .AddSingleton<Tool, PenTool>()
+                .AddSingleton<Tool, SelectTool>()
+                .AddSingleton<Tool, MagicWandTool>()
+                .AddSingleton<Tool, FloodFillTool>()
+                .AddSingleton<Tool, LineTool>()
+                .AddSingleton<Tool, CircleTool>()
+                .AddSingleton<Tool, RectangleTool>()
+                .AddSingleton<Tool, EraserTool>()
+                .AddSingleton<Tool, ColorPickerTool>()
+                .AddSingleton<Tool, BrightnessTool>()
+                .AddSingleton<Tool, ZoomTool>()
+                // Other
+                .AddSingleton<DocumentProvider>();
+    }
+}

+ 57 - 92
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -6,6 +6,8 @@ using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
+using PixiEditor.ViewModels;
+using PixiEditor.ViewModels.SubViewModels.Main;
 using SkiaSharp;
 using System;
 using System.Collections.Generic;
@@ -20,16 +22,19 @@ namespace PixiEditor.Models.Controllers
     [DebuggerDisplay("{Documents.Count} Document(s)")]
     public class BitmapManager : NotifyableObject
     {
+        private readonly ToolsViewModel _tools;
+
         private int previewLayerSize;
-        private Document activeDocument;
-        private Tool selectedTool;
+        private Document activeDocument;
         private Coordinates? startPosition = null;
         private int halfSize;
         private SKColor _highlightColor;
         private PenTool _highlightPen;
 
-        public BitmapManager()
+        public BitmapManager(ToolsViewModel tools)
         {
+            _tools = tools;
+
             MouseController = new MouseMovementController();
             MouseController.StartedRecordingChanges += MouseController_StartedRecordingChanges;
             MouseController.MousePositionChanged += Controller_MousePositionChanged;
@@ -37,7 +42,7 @@ namespace PixiEditor.Models.Controllers
             MouseController.OnMouseDown += MouseController_OnMouseDown;
             MouseController.OnMouseUp += MouseController_OnMouseUp;
             MouseController.OnMouseDownCoordinates += MouseController_OnMouseDownCoordinates;
-            BitmapOperations = new BitmapOperationsUtility(this);
+            BitmapOperations = new BitmapOperationsUtility(this, tools);
             ReadonlyToolUtility = new ReadonlyToolUtility();
             DocumentChanged += BitmapManager_DocumentChanged;
             _highlightPen = new PenTool(this)
@@ -53,38 +58,10 @@ namespace PixiEditor.Models.Controllers
 
         public MouseMovementController MouseController { get; set; }
 
-        public Tool SelectedTool
-        {
-            get => selectedTool;
-            private set
-            {
-                Tool previousTool = selectedTool;
-                if (SetProperty(ref selectedTool, value))
-                {
-                    SelectedToolChanged?.Invoke(this, new SelectedToolEventArgs(previousTool, value));
-                }
-            }
-        }
-
         public Layer ActiveLayer => ActiveDocument.ActiveLayer;
 
         public SKColor PrimaryColor { get; set; }
 
-        public int ToolSize
-        {
-            get => SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize") != null
-            ? SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize").Value
-            : 1;
-            set
-            {
-                if (SelectedTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is SizeSetting toolSize)
-                {
-                    toolSize.Value = value;
-                    HighlightPixels(MousePositionConverter.CurrentCoordinates);
-                }
-            }
-        }
-
         public BitmapOperationsUtility BitmapOperations { get; set; }
 
         public ReadonlyToolUtility ReadonlyToolUtility { get; set; }
@@ -105,14 +82,6 @@ namespace PixiEditor.Models.Controllers
 
 #nullable disable
         public ObservableCollection<Document> Documents { get; set; } = new ObservableCollection<Document>();
-
-        /// <summary>
-        ///     Returns if tool is BitmapOperationTool.
-        /// </summary>
-        public static bool IsOperationTool(Tool tool)
-        {
-            return tool is BitmapOperationTool;
-        }
 
         public void CloseDocument(Document document)
         {
@@ -130,46 +99,34 @@ namespace PixiEditor.Models.Controllers
 
         public void ExecuteTool(Coordinates newPosition, bool clickedOnCanvas)
         {
-            if (SelectedTool.CanStartOutsideCanvas || clickedOnCanvas)
-            {
-                if (startPosition == null)
-                {
-                    SelectedTool.OnStart(newPosition);
-                    startPosition = newPosition;
-                }
+            Tool activeTool = _tools.ActiveTool;
 
-                if (SelectedTool is BitmapOperationTool operationTool)
-                {
-                    BitmapOperations.ExecuteTool(newPosition, MouseController.LastMouseMoveCoordinates, operationTool);
-                }
-                else if (SelectedTool is ReadonlyTool readonlyTool)
-                {
-                    ReadonlyToolUtility.ExecuteTool(
-                        MouseController.LastMouseMoveCoordinates,
-                        readonlyTool);
-                }
-                else
-                {
-                    throw new InvalidOperationException($"'{SelectedTool.GetType().Name}' is either not a Tool or can't inherit '{nameof(Tool)}' directly.\nChanges the base type to either '{nameof(BitmapOperationTool)}' or '{nameof(ReadonlyTool)}'");
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Returns if selected tool is BitmapOperationTool.
-        /// </summary>
-        public bool IsOperationTool()
-        {
-            return IsOperationTool(SelectedTool);
-        }
+            if (activeTool.CanStartOutsideCanvas && !clickedOnCanvas)
+            {
+                return;
+            }
 
-        public void SetActiveTool(Tool tool)
-        {
-            ActiveDocument?.PreviewLayer?.Reset();
-            SelectedTool?.Toolbar.SaveToolbarSettings();
-            SelectedTool = tool;
-            SelectedTool.Toolbar.LoadSharedSettings();
-        }
+            if (startPosition == null)
+            {
+                activeTool.OnStart(newPosition);
+                startPosition = newPosition;
+            }
+
+            if (activeTool is BitmapOperationTool operationTool)
+            {
+                BitmapOperations.ExecuteTool(newPosition, MouseController.LastMouseMoveCoordinates, operationTool);
+            }
+            else if (activeTool is ReadonlyTool readonlyTool)
+            {
+                ReadonlyToolUtility.ExecuteTool(
+                    MouseController.LastMouseMoveCoordinates,
+                    readonlyTool);
+            }
+            else
+            {
+                throw new InvalidOperationException($"'{activeTool.GetType().Name}' is either not a Tool or can't inherit '{nameof(Tool)}' directly.\nChanges the base type to either '{nameof(BitmapOperationTool)}' or '{nameof(ReadonlyTool)}'");
+            }
+        }
 
         private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
         {
@@ -178,7 +135,14 @@ namespace PixiEditor.Models.Controllers
 
         private void Controller_MousePositionChanged(object sender, MouseMovementEventArgs e)
         {
-            SelectedTool.OnMouseMove(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            Tool activeTool = _tools.ActiveTool;
+
+            if (activeTool == null)
+            {
+                return;
+            }
+
+            activeTool.OnMouseMove(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
             if (!MaybeExecuteTool(e.NewPosition) && Mouse.LeftButton == MouseButtonState.Released)
             {
                 HighlightPixels(e.NewPosition);
@@ -187,12 +151,12 @@ namespace PixiEditor.Models.Controllers
 
         private void MouseController_OnMouseDown(object sender, MouseEventArgs e)
         {
-            SelectedTool.OnMouseDown(e);
+            _tools.ActiveTool.OnMouseDown(e);
         }
 
         private void MouseController_OnMouseUp(object sender, MouseEventArgs e)
         {
-            SelectedTool.OnMouseUp(e);
+            _tools.ActiveTool.OnMouseUp(e);
         }
         private void MouseController_OnMouseDownCoordinates(object sender, MouseMovementEventArgs e)
         {
@@ -211,12 +175,12 @@ namespace PixiEditor.Models.Controllers
 
         private bool IsDraggingViewport()
         {
-            return SelectedTool is MoveViewportTool;
+            return _tools.ActiveTool is MoveViewportTool;
         }
 
         private void MouseController_StartedRecordingChanges(object sender, EventArgs e)
         {
-            SelectedTool.OnRecordingLeftMouseDown(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            _tools.ActiveTool.OnRecordingLeftMouseDown(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
             if (ActiveDocument != null)
             {
                 ActiveDocument.PreviewLayer.Reset();
@@ -225,8 +189,9 @@ namespace PixiEditor.Models.Controllers
 
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         {
-            SelectedTool.OnStoppedRecordingMouseUp(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            if (IsOperationTool(SelectedTool) && ((BitmapOperationTool)SelectedTool).RequiresPreviewLayer)
+            Tool selectedTool = _tools.ActiveTool;
+            selectedTool.OnStoppedRecordingMouseUp(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            if (selectedTool is BitmapOperationTool operationTool && operationTool.RequiresPreviewLayer)
             {
                 BitmapOperations.ApplyPreviewLayer();
             }
@@ -236,25 +201,25 @@ namespace PixiEditor.Models.Controllers
             startPosition = null;
         }
 
-        private void HighlightPixels(Coordinates newPosition)
+        public void HighlightPixels(Coordinates newPosition)
         {
-            if (ActiveDocument == null || ActiveDocument.Layers.Count == 0 || SelectedTool.HideHighlight)
+            if (ActiveDocument == null || ActiveDocument.Layers.Count == 0 || _tools.ActiveTool.HideHighlight)
             {
                 return;
             }
 
             var previewLayer = ActiveDocument.PreviewLayer;
 
-            if (ToolSize != previewLayerSize || previewLayer.IsReset)
+            if (_tools.ToolSize != previewLayerSize || previewLayer.IsReset)
             {
-                previewLayerSize = ToolSize;
-                halfSize = (int)Math.Floor(ToolSize / 2f);
-                previewLayer.CreateNewBitmap(ToolSize, ToolSize);
+                previewLayerSize = _tools.ToolSize;
+                halfSize = (int)Math.Floor(_tools.ToolSize / 2f);
+                previewLayer.CreateNewBitmap(_tools.ToolSize, _tools.ToolSize);
 
                 Coordinates cords = new Coordinates(halfSize, halfSize);
 
                 previewLayer.Offset = new Thickness(0, 0, 0, 0);
-                _highlightPen.Draw(previewLayer, cords, cords, _highlightColor, ToolSize);
+                _highlightPen.Draw(previewLayer, cords, cords, _highlightColor, _tools.ToolSize);
 
                 AdjustOffset(newPosition, previewLayer);
 

+ 6 - 2
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -3,6 +3,7 @@ using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
+using PixiEditor.ViewModels.SubViewModels.Main;
 using SkiaSharp;
 using System;
 using System.Collections.Generic;
@@ -18,15 +19,18 @@ namespace PixiEditor.Models.Controllers
 
         private SizeSetting sizeSetting;
 
-        public BitmapOperationsUtility(BitmapManager manager)
+        public BitmapOperationsUtility(BitmapManager manager, ToolsViewModel tools)
         {
             Manager = manager;
+            Tools = tools;
         }
 
         public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
 
         public BitmapManager Manager { get; set; }
 
+        public ToolsViewModel Tools { get; set; }
+
         public void DeletePixels(Layer[] layers, Coordinates[] pixels)
         {
             if (Manager.ActiveDocument == null)
@@ -218,7 +222,7 @@ namespace PixiEditor.Models.Controllers
                     Manager.ActiveDocument.PreviewLayer.ClearCanvas();
                 }
 
-                ((BitmapOperationTool)Manager.SelectedTool).Use(
+                ((BitmapOperationTool)Tools.ActiveTool).Use(
                     Manager.ActiveDocument.PreviewLayer,
                     mouseMove,
                     Manager.PrimaryColor);

+ 16 - 0
PixiEditor/Models/DataHolders/Document/Document.cs

@@ -95,6 +95,22 @@ namespace PixiEditor.Models.DataHolders
             }
         }
 
+        private double mouseXonReference;
+
+        public double MouseXOnReference
+        {
+            get => mouseXonReference;
+            set => SetProperty(ref mouseXonReference, value);
+        }
+
+        private double mouseYonReference;
+
+        public double MouseYOnReference
+        {
+            get => mouseYonReference;
+            set => SetProperty(ref mouseYonReference, value);
+        }
+
         public ExecutionTrigger<Size> CenterViewportTrigger { get; } = new();
         public ExecutionTrigger<double> ZoomViewportTrigger { get; } = new();
 

+ 62 - 0
PixiEditor/Models/Services/DocumentProvider.cs

@@ -0,0 +1,62 @@
+using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
+using System.Collections.Generic;
+
+namespace PixiEditor.Models.Services
+{
+    /// <summary>
+    /// Provides the active document and it's values like active layer and reference layer
+    /// </summary>
+    public class DocumentProvider
+    {
+        private readonly BitmapManager _bitmapManager;
+
+        public DocumentProvider(BitmapManager bitmapManager)
+        {
+            _bitmapManager = bitmapManager;
+        }
+
+        /// <summary>
+        /// Gets all opened documents
+        /// </summary>
+        public IEnumerable<Document> GetDocuments() => _bitmapManager.Documents;
+
+        /// <summary>
+        /// Gets the active document
+        /// </summary>
+        public Document GetDocument() => _bitmapManager.ActiveDocument;
+
+        /// <summary>
+        /// Get the layers of the opened document
+        /// </summary>
+        public IEnumerable<Layer> GetLayers() => _bitmapManager.ActiveDocument?.Layers;
+
+        /// <summary>
+        /// Gets the active layer
+        /// </summary>
+        public Layer GetLayer() => _bitmapManager.ActiveLayer;
+
+        /// <summary>
+        /// Gets the surface of the active layer
+        /// </summary>
+        public Surface GetSurface() => _bitmapManager.ActiveLayer?.LayerBitmap;
+
+        /// <summary>
+        /// Gets the reference layer of the active document
+        /// </summary>
+        public Layer GetReferenceLayer() => _bitmapManager.ActiveDocument?.ReferenceLayer;
+
+        public Surface GetReferenceSurface() => _bitmapManager.ActiveDocument?.ReferenceLayer?.LayerBitmap;
+
+        /// <summary>
+        /// Gets the renderer for the active document
+        /// </summary>
+        public LayerStackRenderer GetRenderer() => _bitmapManager.ActiveDocument?.Renderer;
+
+        /// <summary>
+        /// Gets the renderer for the reference layer of the active document
+        /// </summary>
+        public SingleLayerRenderer GetReferenceRenderer() => _bitmapManager.ActiveDocument?.ReferenceLayerRenderer;
+    }
+}

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

@@ -1,71 +0,0 @@
-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;
-        }
-    }
-}

+ 25 - 15
PixiEditor/Models/Tools/Tools/ColorPickerTool.cs

@@ -1,17 +1,25 @@
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
+using PixiEditor.Models.Services;
 using PixiEditor.ViewModels;
 using SkiaSharp;
+using System;
 using System.Collections.Generic;
-using System.Drawing;
 using System.Linq;
+using System.Windows.Input;
+using static System.Math;
 
 namespace PixiEditor.Models.Tools.Tools
 {
     public class ColorPickerTool : ReadonlyTool
     {
-        public ColorPickerTool()
+        private readonly DocumentProvider _docProvider;
+
+        public ColorPickerTool(DocumentProvider documentProvider)
         {
             ActionDisplay = "Press on pixel to make it the primary color.";
+            _docProvider = documentProvider;
         }
 
         public override bool HideHighlight => true;
@@ -27,24 +35,26 @@ namespace PixiEditor.Models.Tools.Tools
 
         public SKColor GetColorAt(int x, int y)
         {
-            var color = ViewModelMain.Current.BitmapManager?.ActiveDocument?.Renderer?.FinalSurface.GetSRGBPixel(x, y);
-            return color.HasValue ? color.Value : SKColors.Transparent;
-        }
+            SKColor? color;
+            Document activeDocument = _docProvider.GetDocument();
+            Layer referenceLayer;
 
-        public SKColor GetColorUnderMouse()
-        {
-            System.Drawing.Color color;
-            using (Bitmap bitmap = new Bitmap(1, 1))
+            if (Keyboard.IsKeyDown(Key.LeftCtrl) && (referenceLayer = _docProvider.GetReferenceLayer()) is not null)
             {
-                using (Graphics graphics = Graphics.FromImage(bitmap))
-                {
-                    graphics.CopyFromScreen(MousePositionConverter.GetCursorPosition(), new Point(0, 0), new Size(1, 1));
-                }
+                double actualX = activeDocument.MouseXOnReference * referenceLayer.Width / activeDocument.Width;
+                double actualY = activeDocument.MouseYOnReference * referenceLayer.Height / activeDocument.Height;
 
-                color = bitmap.GetPixel(0, 0);
+                x = (int)Round(actualX, MidpointRounding.ToZero);
+                y = (int)Round(actualY, MidpointRounding.ToZero);
+
+                color = referenceLayer.LayerBitmap.GetSRGBPixel(x, y);
+            }
+            else
+            {
+                color = _docProvider.GetRenderer()?.FinalSurface.GetSRGBPixel(x, y);
             }
 
-            return new SKColor(color.R, color.G, color.B, color.A);
+            return color ?? SKColors.Transparent;
         }
     }
 }

+ 2 - 2
PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -53,7 +53,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
 
             Owner.ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
-            Owner.BitmapManager.SelectedTool.OnKeyDown(args);
+            Owner.ToolsSubViewModel.ActiveTool.OnKeyDown(args);
         }
 
         private void MouseDown(object parameter)
@@ -128,7 +128,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 ShortcutController.BlockShortcutExecution = false;
             }
 
-            Owner.BitmapManager.SelectedTool.OnKeyUp(args);
+            Owner.ToolsSubViewModel.ActiveTool.OnKeyUp(args);
         }
     }
 }

+ 3 - 3
PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs

@@ -68,14 +68,14 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             mw.PreviewStylusSystemGesture += Mw_PreviewStylusSystemGesture;
 
             isPenModeEnabled = IPreferences.Current.GetLocalPreference<bool>(nameof(IsPenModeEnabled));
-            Owner.BitmapManager.AddPropertyChangedCallback(nameof(Owner.BitmapManager.SelectedTool), UpdateUseTouchGesture);
+            Owner.ToolsSubViewModel.AddPropertyChangedCallback(nameof(ToolsViewModel.ActiveTool), UpdateUseTouchGesture);
 
             UpdateUseTouchGesture();
         }
 
         private void UpdateUseTouchGesture()
         {
-            if (Owner.BitmapManager.SelectedTool is not (MoveViewportTool or ZoomTool))
+            if (Owner.ToolsSubViewModel.ActiveTool is not (MoveViewportTool or ZoomTool))
             {
                 UseTouchGestures = IsPenModeEnabled;
             }
@@ -101,7 +101,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
             if (e.StylusButton.Guid == StylusPointProperties.TipButton.Id && e.Inverted)
             {
-                PreviousTool = Owner.BitmapManager.SelectedTool;
+                PreviousTool = Owner.ToolsSubViewModel.ActiveTool;
                 Owner.ToolsSubViewModel.SetActiveTool<EraserTool>();
                 ToolSetByStylus = true;
             }

+ 42 - 25
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -1,18 +1,25 @@
 using System;
+using System.Collections;
+using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Reflection;
 using System.Windows.Input;
+using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Helpers;
+using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Enums;
+using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
+using PixiEditor.Models.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
     public class ToolsViewModel : SubViewModel<ViewModelMain>
     {
         private Cursor toolCursor;
+        private Tool activeTool;
 
         public RelayCommand SelectToolCommand { get; set; } // Command that handles tool switching.
 
@@ -20,8 +27,6 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
         public Tool LastActionTool { get; private set; }
 
-        public ObservableCollection<Tool> ToolSet { get; set; }
-
         public Cursor ToolCursor
         {
             get => toolCursor;
@@ -32,26 +37,43 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
         }
 
+        public Tool ActiveTool
+        {
+            get => activeTool;
+            set => SetProperty(ref activeTool, value);
+        }
+
+        public int ToolSize
+        {
+            get => ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize") != null
+            ? ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize").Value
+            : 1;
+            set
+            {
+                if (ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is SizeSetting toolSize)
+                {
+                    toolSize.Value = value;
+                    Owner.BitmapManager.HighlightPixels(MousePositionConverter.CurrentCoordinates);
+                }
+            }
+        }
+
+        public IEnumerable<Tool> ToolSet { get; private set; }
+
         public ToolsViewModel(ViewModelMain owner)
             : base(owner)
         {
             SelectToolCommand = new RelayCommand(SetTool, Owner.DocumentIsNotNull);
             ChangeToolSizeCommand = new RelayCommand(ChangeToolSize);
-
-            Owner.BitmapManager.BitmapOperations.BitmapChanged += (_, _) => TriggerCacheOutdated();
-            Owner.BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
         }
 
         public void SetupTools(IServiceProvider services)
         {
-            ToolSet = new ObservableCollection<Tool>(
-                new ToolBuilder(services)
-                .Add<MoveViewportTool>().Add<MoveTool>().Add<PenTool>().Add<SelectTool>().Add<MagicWandTool>().Add<FloodFillTool>()
-                .Add<LineTool>().Add<CircleTool>().Add<RectangleTool>().Add<EraserTool>().Add<ColorPickerTool>()
-                .Add<BrightnessTool>().Add<ZoomTool>()
-                .Build());
-
-            SetActiveTool<MoveViewportTool>();
+            ToolSet = services.GetServices<Tool>();
+            SetActiveTool<PenTool>();
+
+            Owner.BitmapManager.BitmapOperations.BitmapChanged += (_, _) => TriggerCacheOutdated();
+            Owner.BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
         }
 
         public void SetActiveTool<T>()
@@ -62,15 +84,15 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
         public void SetActiveTool(Tool tool)
         {
-            Tool activeTool = ToolSet.FirstOrDefault(x => x.IsActive);
-            if (activeTool != null)
+            if (ActiveTool != null)
             {
                 activeTool.IsActive = false;
             }
 
+            LastActionTool = ActiveTool;
+            ActiveTool = tool;
+
             tool.IsActive = true;
-            LastActionTool = Owner.BitmapManager.SelectedTool;
-            Owner.BitmapManager.SetActiveTool(tool);
             SetToolCursor(tool.GetType());
 
             if (Owner.StylusSubViewModel != null)
@@ -135,20 +157,15 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         private void ChangeToolSize(object parameter)
         {
             int increment = (int)parameter;
-            int newSize = Owner.BitmapManager.ToolSize + increment;
+            int newSize = ToolSize + increment;
             if (newSize > 0)
             {
-                Owner.BitmapManager.ToolSize = newSize;
+                ToolSize = newSize;
             }
         }
 
         private void SetActiveTool(Type toolType)
         {
-            if (toolType == null && toolType.IsAssignableTo(typeof(Tool)))
-            {
-                return;
-            }
-
             Tool foundTool = ToolSet.First(x => x.GetType() == toolType);
             SetActiveTool(foundTool);
         }
@@ -157,7 +174,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         {
             if (tool != null)
             {
-                ToolCursor = Owner.BitmapManager.SelectedTool.Cursor;
+                ToolCursor = ActiveTool.Cursor;
             }
             else
             {

+ 20 - 46
PixiEditor/ViewModels/ViewModelMain.cs

@@ -87,7 +87,7 @@ namespace PixiEditor.ViewModels
                     return actionDisplay;
                 }
 
-                return BitmapManager.SelectedTool.ActionDisplay;
+                return ToolsSubViewModel.ActiveTool.ActionDisplay;
             }
             set
             {
@@ -118,17 +118,9 @@ namespace PixiEditor.ViewModels
 #endif
         }
 
-        public ViewModelMain(IServiceCollection services)
+        public ViewModelMain(IServiceProvider serviceProvider)
         {
             Current = this;
-
-            ConfigureServices(services);
-            Setup(services.BuildServiceProvider());
-        }
-
-        public void ConfigureServices(IServiceCollection collection)
-        {
-            collection.AddSingleton(this);
         }
 
         public void Setup(IServiceProvider services)
@@ -138,33 +130,32 @@ namespace PixiEditor.ViewModels
             Preferences = services.GetRequiredService<IPreferences>();
 
             Preferences.Init();
-
             BitmapManager = services.GetRequiredService<BitmapManager>();
             BitmapManager.BitmapOperations.BitmapChanged += BitmapUtility_BitmapChanged;
             BitmapManager.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
             BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
 
-            SelectionSubViewModel = new SelectionViewModel(this);
+            SelectionSubViewModel = services.GetService<SelectionViewModel>();
 
             OnStartupCommand = new RelayCommand(OnStartup);
             CloseWindowCommand = new RelayCommand(CloseWindow);
 
-            FileSubViewModel = new FileViewModel(this);
-            ToolsSubViewModel = GetSubViewModel<ToolsViewModel>(services);
-            ToolsSubViewModel.SetupTools(services);
+            FileSubViewModel = services.GetService<FileViewModel>();
+            ToolsSubViewModel = services.GetService<ToolsViewModel>();
+            ToolsSubViewModel?.SetupTools(services);
 
-            IoSubViewModel = new IoViewModel(this);
-            LayersSubViewModel = new LayersViewModel(this);
-            ClipboardSubViewModel = new ClipboardViewModel(this);
-            UndoSubViewModel = new UndoViewModel(this);
-            ViewportSubViewModel = new ViewportViewModel(this);
-            ColorsSubViewModel = new ColorsViewModel(this);
-            DocumentSubViewModel = new DocumentViewModel(this);
-            DiscordViewModel = new DiscordViewModel(this, "764168193685979138");
-            UpdateSubViewModel = new UpdateViewModel(this);
+            IoSubViewModel = services.GetService<IoViewModel>();
+            LayersSubViewModel = services.GetService<LayersViewModel>();
+            ClipboardSubViewModel = services.GetService<ClipboardViewModel>();
+            UndoSubViewModel = services.GetService<UndoViewModel>();
+            ViewportSubViewModel = services.GetService<ViewportViewModel>();
+            ColorsSubViewModel = services.GetService<ColorsViewModel>();
+            DocumentSubViewModel = services.GetService<DocumentViewModel>();
+            DiscordViewModel = services.GetService<DiscordViewModel>();
+            UpdateSubViewModel = services.GetService<UpdateViewModel>();
 
-            WindowSubViewModel = GetSubViewModel<WindowViewModel>(services, false);
-            StylusSubViewModel = GetSubViewModel<StylusViewModel>(services);
+            WindowSubViewModel = services.GetService<WindowViewModel>();
+            StylusSubViewModel = services.GetService<StylusViewModel>();
 
             AddDebugOnlyViewModels();
             AddReleaseOnlyViewModels();
@@ -218,7 +209,7 @@ namespace PixiEditor.ViewModels
                         "View",
                         new Shortcut(Key.OemTilde, ViewportSubViewModel.ToggleGridLinesCommand, "Toggle gridlines", modifier: ModifierKeys.Control)));
 
-            MiscSubViewModel = new MiscViewModel(this);
+            MiscSubViewModel = services.GetService<MiscViewModel>();
 
             // Add F1 shortcut after MiscSubViewModel is constructed
             ShortcutController.ShortcutGroups.Add(
@@ -371,33 +362,16 @@ namespace PixiEditor.ViewModels
 
         private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
         {
-            UndoSubViewModel.TriggerNewUndoChange(BitmapManager.SelectedTool);
+            UndoSubViewModel.TriggerNewUndoChange(ToolsSubViewModel.ActiveTool);
         }
 
         private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
         {
             BitmapManager.ActiveDocument.ChangesSaved = false;
-            if (BitmapManager.IsOperationTool())
+            if (ToolsSubViewModel.ActiveTool is BitmapOperationTool)
             {
                 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;
-        }
     }
 }

+ 5 - 5
PixiEditor/Views/MainWindow.xaml

@@ -235,9 +235,9 @@
             </ToggleButton>
             <Grid Margin="5,5,10,5" Background="{StaticResource BrighterAccentColor}" Width="5"/>
             <Label Style="{StaticResource BaseLabel}" FontSize="12"
-                   VerticalAlignment="Center" Content="{Binding BitmapManager.SelectedTool.DisplayName}"
-                   ToolTip="{Binding BitmapManager.SelectedTool.ActionDisplay}"/>
-            <ItemsControl ItemsSource="{Binding BitmapManager.SelectedTool.Toolbar.Settings}">
+                   VerticalAlignment="Center" Content="{Binding ToolsSubViewModel.ActiveTool.DisplayName}"
+                   ToolTip="{Binding ToolsSubViewModel.ActiveTool.ActionDisplay}"/>
+            <ItemsControl ItemsSource="{Binding ToolsSubViewModel.ActiveTool.Toolbar.Settings}">
                 <ItemsControl.ItemsPanel>
                     <ItemsPanelTemplate>
                         <StackPanel Orientation="Horizontal" Margin="10, 0, 0, 0" />
@@ -287,8 +287,8 @@
                                         MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
                                         MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}"
                                         UseTouchGestures="{Binding XamlAccesibleViewModel.StylusSubViewModel.UseTouchGestures}"
-                                        IsUsingZoomTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={converters:IsSpecifiedTypeConverter SpecifiedType={x:Type tools:ZoomTool}}}"
-                                        IsUsingMoveViewportTool="{Binding XamlAccesibleViewModel.BitmapManager.SelectedTool, Converter={converters:IsSpecifiedTypeConverter SpecifiedType={x:Type tools:MoveViewportTool}}}"
+                                        IsUsingZoomTool="{Binding XamlAccesibleViewModel.ToolsSubViewModel.ActiveTool, Converter={converters:IsSpecifiedTypeConverter SpecifiedType={x:Type tools:ZoomTool}}}"
+                                        IsUsingMoveViewportTool="{Binding XamlAccesibleViewModel.ToolsSubViewModel.ActiveTool, Converter={converters:IsSpecifiedTypeConverter SpecifiedType={x:Type tools:MoveViewportTool}}}"
                                         Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False">
                                         <i:Interaction.Triggers>
                                             <i:EventTrigger EventName="PreviewMouseDown">

+ 13 - 24
PixiEditor/Views/MainWindow.xaml.cs

@@ -1,18 +1,17 @@
 using Microsoft.Extensions.DependencyInjection;
+using PixiEditor.Helpers.Extensions;
+using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels;
+using PixiEditor.Views.Dialogs;
 using System;
 using System.ComponentModel;
-using System.Windows;
-using System.Windows.Input;
-using PixiEditor.ViewModels.SubViewModels.Main;
 using System.Diagnostics;
 using System.Linq;
-using PixiEditor.Views.Dialogs;
-using System.Windows.Media.Imaging;
-using PixiEditor.Models.DataHolders;
+using System.Windows;
+using System.Windows.Input;
 using System.Windows.Interop;
-using PixiEditor.Models.Controllers;
+using System.Windows.Media.Imaging;
 
 namespace PixiEditor
 {
@@ -23,17 +22,19 @@ namespace PixiEditor
     {
         private static WriteableBitmap pixiEditorLogo;
 
-        private readonly PreferencesSettings preferences;
+        private readonly IPreferences preferences;
 
         public new ViewModelMain DataContext { get => (ViewModelMain)base.DataContext; set => base.DataContext = value; }
 
         public MainWindow()
         {
-            preferences = new PreferencesSettings();
+            IServiceProvider services = new ServiceCollection()
+                .AddPixiEditor()
+                .BuildServiceProvider();
 
-            IServiceCollection services = ConfigureServices(new ServiceCollection());
-
-            DataContext = new ViewModelMain(services);
+            preferences = services.GetRequiredService<IPreferences>();
+            DataContext = services.GetRequiredService<ViewModelMain>();
+            DataContext.Setup(services);
 
             InitializeComponent();
 
@@ -78,18 +79,6 @@ namespace PixiEditor
             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)
         {
             if (preferences.GetPreference("ImagePreviewInTaskbar", false))

+ 20 - 10
PixiEditor/Views/UserControls/DrawingViewPort.xaml

@@ -33,15 +33,15 @@
         </i:Interaction.Triggers>
         <i:Interaction.Behaviors>
             <behaviors:MouseBehavior RelativeTo="{Binding ElementName=zoombox, Path=AdditionalContent}"
-                                                  MouseX="{Binding MouseXOnCanvas, Mode=TwoWay, ElementName=uc}"
-                                                  MouseY="{Binding MouseYOnCanvas, Mode=TwoWay, ElementName=uc}" />
+                                     MouseX="{Binding MouseXOnCanvas, Mode=TwoWay, ElementName=uc}"
+                                     MouseY="{Binding MouseYOnCanvas, Mode=TwoWay, ElementName=uc}" />
         </i:Interaction.Behaviors>
 
         <local:Zoombox.AdditionalContent>
-            <Canvas Width="{Binding Width}"
-                                Loaded="OnCanvasLoaded"
-                                Height="{Binding Height}" VerticalAlignment="Center"
-                                HorizontalAlignment="Center" RenderOptions.BitmapScalingMode="NearestNeighbor">
+            <Canvas Width="{Binding Width}" Height="{Binding Height}"
+                    Loaded="OnCanvasLoaded"
+                    VerticalAlignment="Center" HorizontalAlignment="Center"
+                    RenderOptions.BitmapScalingMode="NearestNeighbor">
                 <Canvas.Background>
                     <ImageBrush ImageSource="/Images/CheckerTile.png" TileMode="Tile" ViewportUnits="Absolute">
                         <ImageBrush.Viewport>
@@ -53,12 +53,22 @@
                         </ImageBrush.Viewport>
                     </ImageBrush>
                 </Canvas.Background>
+
+                <!--<local:ReferenceLayerView Document="{Binding}"/>-->
+
                 <Image Source="{Binding ReferenceLayerRenderer.FinalBitmap}"
-                       VerticalAlignment="Center" Stretch="Uniform"
+                       VerticalAlignment="Stretch" Stretch="Uniform"
                        Visibility="{Binding ReferenceLayer.IsVisible, Converter={BoolToVisibilityConverter}}"
-                       HorizontalAlignment="Center" Width="{Binding Width}"
-                       Height="{Binding Height}"
-                       RenderOptions.BitmapScalingMode="NearestNeighbor"/>
+                       HorizontalAlignment="Stretch"
+                       Width="{Binding Width}" Height="{Binding Height}"
+                       RenderOptions.BitmapScalingMode="NearestNeighbor">
+                    <i:Interaction.Behaviors>
+                        <behaviors:MouseBehavior
+                                     MouseX="{Binding MouseXOnReference, Mode=TwoWay}"
+                                     MouseY="{Binding MouseYOnReference, Mode=TwoWay}" />
+                    </i:Interaction.Behaviors>
+                </Image>
+
                 <Image Source="{Binding PreviewLayerRenderer.FinalBitmap}" Panel.ZIndex="2"
                                    RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform"
                                    Width="{Binding Width}"