Browse Source

Redesign the input system and tool execution

Equbuxu 3 years ago
parent
commit
b03ff7e75e
39 changed files with 951 additions and 1025 deletions
  1. 85 0
      PixiEditor/Helpers/CoordinatesHelper.cs
  2. 1 0
      PixiEditor/Helpers/GlobalMouseHook.cs
  3. 1 1
      PixiEditor/Helpers/RelayCommand.cs
  4. 127 159
      PixiEditor/Models/Controllers/BitmapManager.cs
  5. 32 168
      PixiEditor/Models/Controllers/BitmapOperationsUtility.cs
  6. 15 0
      PixiEditor/Models/Controllers/ICanvasInputTarget.cs
  7. 53 0
      PixiEditor/Models/Controllers/MouseInputFilter.cs
  8. 0 94
      PixiEditor/Models/Controllers/MouseMovementController.cs
  9. 0 14
      PixiEditor/Models/Controllers/ReadonlyToolUtility.cs
  10. 97 0
      PixiEditor/Models/Controllers/ToolSession.cs
  11. 140 0
      PixiEditor/Models/Controllers/ToolSessionController.cs
  12. 0 2
      PixiEditor/Models/DataHolders/Document/Document.cs
  13. 16 20
      PixiEditor/Models/Tools/BitmapOperationTool.cs
  14. 4 4
      PixiEditor/Models/Tools/ReadonlyTool.cs
  15. 0 5
      PixiEditor/Models/Tools/ShapeTool.cs
  16. 16 55
      PixiEditor/Models/Tools/Tool.cs
  17. 17 31
      PixiEditor/Models/Tools/Tools/BrightnessTool.cs
  18. 30 37
      PixiEditor/Models/Tools/Tools/CircleTool.cs
  19. 10 32
      PixiEditor/Models/Tools/Tools/ColorPickerTool.cs
  20. 6 9
      PixiEditor/Models/Tools/Tools/EraserTool.cs
  21. 7 8
      PixiEditor/Models/Tools/Tools/FloodFillTool.cs
  22. 91 97
      PixiEditor/Models/Tools/Tools/LineTool.cs
  23. 13 22
      PixiEditor/Models/Tools/Tools/MagicWandTool.cs
  24. 53 67
      PixiEditor/Models/Tools/Tools/MoveTool.cs
  25. 4 5
      PixiEditor/Models/Tools/Tools/MoveViewportTool.cs
  26. 12 13
      PixiEditor/Models/Tools/Tools/PenTool.cs
  27. 11 22
      PixiEditor/Models/Tools/Tools/RectangleTool.cs
  28. 12 16
      PixiEditor/Models/Tools/Tools/SelectTool.cs
  29. 6 7
      PixiEditor/Models/Tools/Tools/ZoomTool.cs
  30. 45 79
      PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs
  31. 4 6
      PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs
  32. 11 15
      PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  33. 3 16
      PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs
  34. 4 9
      PixiEditor/ViewModels/ViewModelMain.cs
  35. 1 0
      PixiEditor/Views/MainWindow.xaml
  36. 5 2
      PixiEditor/Views/UserControls/DrawingViewPort.xaml
  37. 10 1
      PixiEditor/Views/UserControls/DrawingViewPort.xaml.cs
  38. 2 2
      PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs
  39. 7 7
      PixiEditorTests/ModelsTests/ControllersTests/MouseMovementControllerTests.cs

+ 85 - 0
PixiEditor/Helpers/CoordinatesHelper.cs

@@ -0,0 +1,85 @@
+using PixiEditor.Models.Position;
+using System;
+using System.Collections.Generic;
+
+namespace PixiEditor.Helpers
+{
+    internal class CoordinatesHelper
+    {
+        public static (Coordinates, Coordinates) GetSquareOrLineCoordinates(IReadOnlyList<Coordinates> coords)
+        {
+            if (DoCoordsFormLine(coords))
+            {
+                return GetLineCoordinates(coords);
+            }
+            return GetSquareCoordiantes(coords);
+        }
+
+        private static bool DoCoordsFormLine(IReadOnlyList<Coordinates> coords)
+        {
+            var p1 = coords[0];
+            var p2 = coords[^1];
+            //find delta and mirror to first quadrant
+            float dX = Math.Abs(p2.X - p1.X);
+            float dY = Math.Abs(p2.Y - p1.Y);
+
+            //normalize
+            float length = (float)Math.Sqrt(dX * dX + dY * dY);
+            if (length == 0)
+                return false;
+            dX = dX / length;
+            dY = dY / length;
+
+            return dX < 0.25f || dY < 0.25f; //angle < 15 deg or angle > 75 deg (sin 15 ~= 0.25)
+        }
+
+        public static (Coordinates, Coordinates) GetLineCoordinates(IReadOnlyList<Coordinates> mouseMoveCords)
+        {
+            int xStart = mouseMoveCords[0].X;
+            int yStart = mouseMoveCords[0].Y;
+
+            int xEnd = mouseMoveCords[^1].X;
+            int yEnd = mouseMoveCords[^1].Y;
+
+
+            if (Math.Abs(xStart - xEnd) > Math.Abs(yStart - yEnd))
+            {
+                yEnd = yStart;
+            }
+            else
+            {
+                xEnd = xStart;
+            }
+            return (new(xStart, yStart), new(xEnd, yEnd));
+        }
+
+        /// <summary>
+        ///     Extracts square from rectangle mouse drag, used to draw symmetric shapes.
+        /// </summary>
+        public static (Coordinates, Coordinates) GetSquareCoordiantes(IReadOnlyList<Coordinates> mouseMoveCords)
+        {
+            var end = mouseMoveCords[^1];
+            var start = mouseMoveCords[0];
+
+            //find delta and mirror to first quadrant
+            var dX = Math.Abs(start.X - end.X);
+            var dY = Math.Abs(start.Y - end.Y);
+
+            float sqrt2 = (float)Math.Sqrt(2);
+            //vector of length 1 at 45 degrees;
+            float diagX, diagY;
+            diagX = diagY = 1 / sqrt2;
+
+            //dot product of delta and diag, returns length of [delta projected onto diag]
+            float projectedLength = diagX * dX + diagY * dY;
+            //project above onto axes
+            float axisLength = projectedLength / sqrt2;
+
+            //final coords
+            float x = -Math.Sign(start.X - end.X) * axisLength;
+            float y = -Math.Sign(start.Y - end.Y) * axisLength;
+            end = new Coordinates((int)x + start.X, (int)y + start.Y);
+            return (start, end);
+        }
+    }
+}

+ 1 - 0
PixiEditor/Helpers/GlobalMouseHook.cs

@@ -88,6 +88,7 @@ namespace PixiEditor.Helpers
                 {
                 {
                     if (MouseUp != null)
                     if (MouseUp != null)
                     {
                     {
+
                         MouseButton button = wParam == WM_LBUTTONUP ? MouseButton.Left
                         MouseButton button = wParam == WM_LBUTTONUP ? MouseButton.Left
                             : wParam == WM_MBUTTONUP ? MouseButton.Middle : MouseButton.Right;
                             : wParam == WM_MBUTTONUP ? MouseButton.Middle : MouseButton.Right;
                         MouseUp.Invoke(null, new Point(mouseHookStruct.Pt.X, mouseHookStruct.Pt.Y), button);
                         MouseUp.Invoke(null, new Point(mouseHookStruct.Pt.X, mouseHookStruct.Pt.Y), button);

+ 1 - 1
PixiEditor/Helpers/RelayCommand.cs

@@ -40,4 +40,4 @@ namespace PixiEditor.Helpers
             execute(parameter);
             execute(parameter);
         }
         }
     }
     }
-}
+}

+ 127 - 159
PixiEditor/Models/Controllers/BitmapManager.cs

@@ -3,98 +3,92 @@ using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools;
+using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
-using PixiEditor.ViewModels;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using SkiaSharp;
 using SkiaSharp;
 using System;
 using System;
-using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
 using System.Diagnostics;
-using System.Linq;
 using System.Windows;
 using System.Windows;
-using System.Windows.Input;
-
+
 namespace PixiEditor.Models.Controllers
 namespace PixiEditor.Models.Controllers
 {
 {
     [DebuggerDisplay("{Documents.Count} Document(s)")]
     [DebuggerDisplay("{Documents.Count} Document(s)")]
     public class BitmapManager : NotifyableObject
     public class BitmapManager : NotifyableObject
     {
     {
-        private readonly ToolsViewModel _tools;
-
-        private int previewLayerSize;
-        private Document activeDocument;
-        private Coordinates? startPosition = null;
-        private int halfSize;
-        private SKColor _highlightColor;
-        private PenTool _highlightPen;
-        private bool hideReferenceLayer;
-        private bool onlyReferenceLayer;
-
-        public BitmapManager(ToolsViewModel tools)
-        {
-            _tools = tools;
-
-            MouseController = new MouseMovementController();
-            MouseController.StartedRecordingChanges += MouseController_StartedRecordingChanges;
-            MouseController.MousePositionChanged += Controller_MousePositionChanged;
-            MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
-            MouseController.OnMouseDown += MouseController_OnMouseDown;
-            MouseController.OnMouseUp += MouseController_OnMouseUp;
-            MouseController.OnMouseDownCoordinates += MouseController_OnMouseDownCoordinates;
-            BitmapOperations = new BitmapOperationsUtility(this, tools);
-            ReadonlyToolUtility = new ReadonlyToolUtility();
-            DocumentChanged += BitmapManager_DocumentChanged;
-            _highlightPen = new PenTool(this)
-            {
-                AutomaticallyResizeCanvas = false
-            };
-            _highlightColor = new SKColor(0, 0, 0, 77);
-        }
+        private ToolSessionController ToolSessionController { get; set; }
+        public ICanvasInputTarget InputTarget => ToolSessionController;
+        public BitmapOperationsUtility BitmapOperations { get; set; }
 
 
-        public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
-
-        public MouseMovementController MouseController { get; set; }
-
-        public Layer ActiveLayer => ActiveDocument.ActiveLayer;
-
-        public SKColor PrimaryColor { get; set; }
+        public ObservableCollection<Document> Documents { get; set; } = new ObservableCollection<Document>();
 
 
-        public BitmapOperationsUtility BitmapOperations { get; set; }
-
-        public ReadonlyToolUtility ReadonlyToolUtility { get; set; }
-
-#nullable enable
+        private Document activeDocument;
         public Document ActiveDocument
         public Document ActiveDocument
         {
         {
             get => activeDocument;
             get => activeDocument;
             set
             set
             {
             {
                 activeDocument?.UpdatePreviewImage();
                 activeDocument?.UpdatePreviewImage();
-                Document? oldDoc = activeDocument;
+                Document oldDoc = activeDocument;
                 activeDocument = value;
                 activeDocument = value;
                 RaisePropertyChanged(nameof(ActiveDocument));
                 RaisePropertyChanged(nameof(ActiveDocument));
                 DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
                 DocumentChanged?.Invoke(this, new DocumentChangedEventArgs(value, oldDoc));
             }
             }
         }
         }
 
 
-#nullable disable
-        public ObservableCollection<Document> Documents { get; set; } = new ObservableCollection<Document>();
+        public event EventHandler<DocumentChangedEventArgs> DocumentChanged;
+        public event EventHandler StopUsingTool;
+
+        public Layer ActiveLayer => ActiveDocument.ActiveLayer;
+
+        public SKColor PrimaryColor { get; set; }
 
 
+        private bool hideReferenceLayer;
         public bool HideReferenceLayer
         public bool HideReferenceLayer
         {
         {
             get => hideReferenceLayer;
             get => hideReferenceLayer;
             set => SetProperty(ref hideReferenceLayer, value);
             set => SetProperty(ref hideReferenceLayer, value);
         }
         }
 
 
+        private bool onlyReferenceLayer;
         public bool OnlyReferenceLayer
         public bool OnlyReferenceLayer
         {
         {
             get => onlyReferenceLayer;
             get => onlyReferenceLayer;
             set => SetProperty(ref onlyReferenceLayer, value);
             set => SetProperty(ref onlyReferenceLayer, value);
         }
         }
 
 
+        private readonly ToolsViewModel _tools;
+
+        private int previewLayerSize;
+        private int halfSize;
+        private SKColor _highlightColor;
+        private PenTool _highlightPen;
+
+        private ToolSession activeSession = null;
+
+
+        public BitmapManager(ToolsViewModel tools)
+        {
+            _tools = tools;
+
+            ToolSessionController = new ToolSessionController();
+            ToolSessionController.SessionStarted += OnSessionStart;
+            ToolSessionController.SessionEnded += OnSessionEnd;
+            ToolSessionController.PixelMousePositionChanged += OnPixelMousePositionChange;
+            ToolSessionController.PreciseMousePositionChanged += OnPreciseMousePositionChange;
+            ToolSessionController.KeyStateChanged += (_, _) => UpdateActionDisplay();
+            BitmapOperations = new BitmapOperationsUtility(this, tools);
+
+            DocumentChanged += BitmapManager_DocumentChanged;
+
+            _highlightPen = new PenTool(this)
+            {
+                AutomaticallyResizeCanvas = false
+            };
+            _highlightColor = new SKColor(0, 0, 0, 77);
+        }
+
         public void CloseDocument(Document document)
         public void CloseDocument(Document document)
         {
         {
             int nextIndex = 0;
             int nextIndex = 0;
@@ -107,41 +101,91 @@ namespace PixiEditor.Models.Controllers
             Documents.Remove(document);
             Documents.Remove(document);
             ActiveDocument = nextIndex >= 0 ? Documents[nextIndex] : null;
             ActiveDocument = nextIndex >= 0 ? Documents[nextIndex] : null;
             document.Dispose();
             document.Dispose();
+        }
+
+        public void UpdateActionDisplay()
+        {
+            _tools.ActiveTool?.UpdateActionDisplay(ToolSessionController.IsCtrlDown, ToolSessionController.IsShiftDown, ToolSessionController.IsAltDown);
+        }
+
+        private void OnSessionStart(object sender, ToolSession e)
+        {
+            activeSession = e;
+
+            ActiveDocument.PreviewLayer.Reset();
+            ExecuteTool();
         }
         }
 
 
-        public void ExecuteTool(Coordinates newPosition, bool clickedOnCanvas)
-        {
-            Tool activeTool = _tools.ActiveTool;
-
-            if (activeTool.CanStartOutsideCanvas && !clickedOnCanvas)
+        private void OnSessionEnd(object sender, ToolSession e)
+        {
+            activeSession = null;
+
+            if (e.Tool is BitmapOperationTool operationTool && operationTool.RequiresPreviewLayer)
             {
             {
-                return;
+                BitmapOperations.ApplyPreviewLayer();
             }
             }
 
 
-            if (startPosition == null)
+            ActiveDocument.PreviewLayer.Reset();
+            HighlightPixels(MousePositionConverter.CurrentCoordinates);
+            StopUsingTool?.Invoke(this, EventArgs.Empty);
+        }
+
+        private void OnPreciseMousePositionChange(object sender, (double, double) e)
+        {
+            if (activeSession == null || !activeSession.Tool.RequiresPreciseMouseData)
+                return;
+            ExecuteTool();
+        }
+
+        private void OnPixelMousePositionChange(object sender, MouseMovementEventArgs e)
+        {
+            if (activeSession != null)
             {
             {
-                activeTool.OnStart(newPosition);
-                startPosition = newPosition;
+                if (activeSession.Tool.RequiresPreciseMouseData)
+                    return;
+                ExecuteTool();
+                return;
+            }
+            else
+            {
+                HighlightPixels(e.NewPosition);
             }
             }
+        }
+
+        private void ExecuteTool()
+        {
+            if (activeSession == null)
+                throw new Exception("Can't execute tool's Use outside a session");
 
 
-            if (activeTool is BitmapOperationTool operationTool)
+            if (activeSession.Tool is BitmapOperationTool operationTool)
             {
             {
-                BitmapOperations.ExecuteTool(newPosition, MouseController.LastMouseMoveCoordinates, operationTool);
+                BitmapOperations.UseTool(activeSession.MouseMovement, operationTool, PrimaryColor);
             }
             }
-            else if (activeTool is ReadonlyTool readonlyTool)
+            else if (activeSession.Tool is ReadonlyTool readonlyTool)
             {
             {
-                ActiveDocument.PreviewLayer.Reset();
-                ReadonlyToolUtility.ExecuteTool(
-                    MouseController.LastMouseMoveCoordinates,
-                    readonlyTool);
+                readonlyTool.Use(activeSession.MouseMovement);
             }
             }
             else
             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)}'");
+                throw new InvalidOperationException($"'{activeSession.Tool.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)}'");
             }
             }
-        }
-
-        public void HighlightPixels(Coordinates newPosition)
+        }
+
+        private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
+        {
+            e.NewDocument?.GeneratePreviewLayer();
+            if (e.OldDocument != e.NewDocument)
+                ToolSessionController.ForceStopActiveSessionIfAny();
+        }
+
+        public void UpdateHighlightIfNecessary(bool forceHide = false)
+        {
+            if (activeSession != null)
+                return;
+            HighlightPixels(forceHide ? new(-1, -1) : ToolSessionController.LastPixelPosition);
+        }
+
+        private void HighlightPixels(Coordinates newPosition)
         {
         {
             if (ActiveDocument == null || ActiveDocument.Layers.Count == 0 || _tools.ActiveTool.HideHighlight)
             if (ActiveDocument == null || ActiveDocument.Layers.Count == 0 || _tools.ActiveTool.HideHighlight)
             {
             {
@@ -150,6 +194,15 @@ namespace PixiEditor.Models.Controllers
 
 
             var previewLayer = ActiveDocument.PreviewLayer;
             var previewLayer = ActiveDocument.PreviewLayer;
 
 
+            if (newPosition.X > ActiveDocument.Width
+                || newPosition.Y > ActiveDocument.Height
+                || newPosition.X < 0 || newPosition.Y < 0)
+            {
+                previewLayer.Reset();
+                previewLayerSize = -1;
+                return;
+            }
+
             if (_tools.ToolSize != previewLayerSize || previewLayer.IsReset)
             if (_tools.ToolSize != previewLayerSize || previewLayer.IsReset)
             {
             {
                 previewLayerSize = _tools.ToolSize;
                 previewLayerSize = _tools.ToolSize;
@@ -160,97 +213,12 @@ namespace PixiEditor.Models.Controllers
 
 
                 previewLayer.Offset = new Thickness(0, 0, 0, 0);
                 previewLayer.Offset = new Thickness(0, 0, 0, 0);
                 _highlightPen.Draw(previewLayer, cords, cords, _highlightColor, _tools.ToolSize);
                 _highlightPen.Draw(previewLayer, cords, cords, _highlightColor, _tools.ToolSize);
-
-                AdjustOffset(newPosition, previewLayer);
-
             }
             }
-
-            previewLayer.InvokeLayerBitmapChange();
-
             AdjustOffset(newPosition, previewLayer);
             AdjustOffset(newPosition, previewLayer);
-
-            if (newPosition.X > ActiveDocument.Width
-                || newPosition.Y > ActiveDocument.Height
-                || newPosition.X < 0 || newPosition.Y < 0)
-            {
-                previewLayer.Reset();
-                previewLayerSize = -1;
-            }
-        }
-
-        private void BitmapManager_DocumentChanged(object sender, DocumentChangedEventArgs e)
-        {
-            e.NewDocument?.GeneratePreviewLayer();
-        }
 
 
-        private void Controller_MousePositionChanged(object sender, MouseMovementEventArgs e)
-        {
-            Tool activeTool = _tools.ActiveTool;
-
-            if (activeTool == null)
-            {
-                return;
-            }
-
-            activeTool.OnMouseMove(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            if (!MaybeExecuteTool(e.NewPosition) && MouseController.LeftMouseState == MouseButtonState.Released)
-            {
-                HighlightPixels(e.NewPosition);
-            }
-        }
-
-        private void MouseController_OnMouseDown(object sender, MouseEventArgs e)
-        {
-            _tools.ActiveTool.OnMouseDown(e);
-        }
-
-        private void MouseController_OnMouseUp(object sender, MouseEventArgs e)
-        {
-            _tools.ActiveTool.OnMouseUp(e);
-        }
-        private void MouseController_OnMouseDownCoordinates(object sender, MouseMovementEventArgs e)
-        {
-            MaybeExecuteTool(e.NewPosition);
-        }
-
-        private bool MaybeExecuteTool(Coordinates newPosition)
-        {
-            if (MouseController.LeftMouseState == MouseButtonState.Pressed && !IsDraggingViewport() && ActiveDocument != null)
-            {
-                ExecuteTool(newPosition, MouseController.ClickedOnCanvas);
-                return true;
-            }
-            return false;
-        }
-
-        private bool IsDraggingViewport()
-        {
-            return _tools.ActiveTool is MoveViewportTool;
-        }
-
-        private void MouseController_StartedRecordingChanges(object sender, EventArgs e)
-        {
-            _tools.ActiveTool.OnRecordingLeftMouseDown(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            if (ActiveDocument != null)
-            {
-                ActiveDocument.PreviewLayer.Reset();
-            }
+            previewLayer.InvokeLayerBitmapChange();
         }
         }
 
 
-        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
-        {
-            Tool selectedTool = _tools.ActiveTool;
-            selectedTool.OnStoppedRecordingMouseUp(new MouseEventArgs(Mouse.PrimaryDevice, (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
-            if (selectedTool is BitmapOperationTool operationTool && operationTool.RequiresPreviewLayer)
-            {
-                BitmapOperations.ApplyPreviewLayer();
-            }
-
-            HighlightPixels(MousePositionConverter.CurrentCoordinates);
-
-            startPosition = null;
-        }
-
         private void AdjustOffset(Coordinates newPosition, Layer previewLayer)
         private void AdjustOffset(Coordinates newPosition, Layer previewLayer)
         {
         {
             Coordinates start = newPosition - halfSize;
             Coordinates start = newPosition - halfSize;

+ 32 - 168
PixiEditor/Models/Controllers/BitmapOperationsUtility.cs

@@ -2,23 +2,24 @@
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using SkiaSharp;
 using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows;
 using System.Windows;
-using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Controllers
 namespace PixiEditor.Models.Controllers
 {
 {
     public class BitmapOperationsUtility
     public class BitmapOperationsUtility
     {
     {
-        private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
+        public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
+
+        public BitmapManager Manager { get; set; }
 
 
+        public ToolsViewModel Tools { get; set; }
 
 
-        private SizeSetting sizeSetting;
+        private SKPaint BlendingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
 
 
         public BitmapOperationsUtility(BitmapManager manager, ToolsViewModel tools)
         public BitmapOperationsUtility(BitmapManager manager, ToolsViewModel tools)
         {
         {
@@ -26,12 +27,6 @@ namespace PixiEditor.Models.Controllers
             Tools = tools;
             Tools = tools;
         }
         }
 
 
-        public event EventHandler<BitmapChangedEventArgs> BitmapChanged;
-
-        public BitmapManager Manager { get; set; }
-
-        public ToolsViewModel Tools { get; set; }
-
         public void DeletePixels(Layer[] layers, Coordinates[] pixels)
         public void DeletePixels(Layer[] layers, Coordinates[] pixels)
         {
         {
             if (Manager.ActiveDocument == null)
             if (Manager.ActiveDocument == null)
@@ -40,19 +35,11 @@ namespace PixiEditor.Models.Controllers
             }
             }
 
 
             StorageBasedChange change = new StorageBasedChange(Manager.ActiveDocument, layers, true);
             StorageBasedChange change = new StorageBasedChange(Manager.ActiveDocument, layers, true);
-            
 
 
-            // TODO: Fix
             BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
             BitmapPixelChanges changes = BitmapPixelChanges.FromSingleColoredArray(pixels, SKColors.Empty);
-            //Dictionary<Guid, SKColor[]> oldValues = BitmapUtils.GetPixelsForSelection(layers, pixels);
-            //LayerChange[] old = new LayerChange[layers.Length];
-            //LayerChange[] newChange = new LayerChange[layers.Length];
             for (int i = 0; i < layers.Length; i++)
             for (int i = 0; i < layers.Length; i++)
             {
             {
                 Guid guid = layers[i].LayerGuid;
                 Guid guid = layers[i].LayerGuid;
-                //old[i] = new LayerChange(
-                    //BitmapPixelChanges.FromArrays(pixels, oldValues[layers[i].LayerGuid]), guid);
-                //newChange[i] = new LayerChange(changes, guid);
                 layers[i].SetPixels(changes);
                 layers[i].SetPixels(changes);
             }
             }
 
 
@@ -60,22 +47,37 @@ namespace PixiEditor.Models.Controllers
             Manager.ActiveDocument.UndoManager.AddUndoChange(change.ToChange(StorageBasedChange.BasicUndoProcess, args, "Delete selected pixels"));
             Manager.ActiveDocument.UndoManager.AddUndoChange(change.ToChange(StorageBasedChange.BasicUndoProcess, args, "Delete selected pixels"));
         }
         }
 
 
-        /// <summary>
-        ///     Executes tool Use() method with given parameters. NOTE: [0] is a start point, [^1] is latest.
-        /// </summary>
-        /// <param name="newPos">Most recent coordinates.</param>
-        /// <param name="mouseMove">Last mouse movement coordinates.</param>
-        /// <param name="tool">Tool to execute.</param>
-        public void ExecuteTool(Coordinates newPos, List<Coordinates> mouseMove, BitmapOperationTool tool)
+        public void UseTool(IReadOnlyList<Coordinates> recordedMouseMovement, BitmapOperationTool tool, SKColor color)
         {
         {
-            if (Manager.ActiveDocument != null && tool != null)
+            if (Manager.ActiveDocument.Layers.Count == 0)
+                return;
+
+            if (!tool.RequiresPreviewLayer)
+            {
+                tool.Use(Manager.ActiveLayer, null, Manager.ActiveDocument.Layers, recordedMouseMovement, color);
+                BitmapChanged?.Invoke(this, null);
+            }
+            else
             {
             {
-                if (Manager.ActiveDocument.Layers.Count == 0 || mouseMove.Count == 0)
+                UseToolOnPreviewLayer(recordedMouseMovement, tool.ClearPreviewLayerOnEachIteration);
+            }
+        }
+
+        private void UseToolOnPreviewLayer(IReadOnlyList<Coordinates> recordedMouseMovement, bool clearPreviewLayer)
+        {
+            if (recordedMouseMovement.Count > 0)
+            {
+                if (clearPreviewLayer)
                 {
                 {
-                    return;
+                    Manager.ActiveDocument.PreviewLayer.ClearCanvas();
                 }
                 }
 
 
-                UseTool(mouseMove, tool, Manager.PrimaryColor);
+                ((BitmapOperationTool)Tools.ActiveTool).Use(
+                    Manager.ActiveLayer,
+                    Manager.ActiveDocument.PreviewLayer,
+                    Manager.ActiveDocument.Layers,
+                    recordedMouseMovement,
+                    Manager.PrimaryColor);
             }
             }
         }
         }
 
 
@@ -95,147 +97,9 @@ namespace PixiEditor.Models.Controllers
                     previewLayer.OffsetY - activeLayer.OffsetY,
                     previewLayer.OffsetY - activeLayer.OffsetY,
                     BlendingPaint
                     BlendingPaint
                 );
                 );
+
             Manager.ActiveLayer.InvokeLayerBitmapChange(dirtyRect);
             Manager.ActiveLayer.InvokeLayerBitmapChange(dirtyRect);
-            // Don't forget about firing BitmapChanged
             BitmapChanged?.Invoke(this, null);
             BitmapChanged?.Invoke(this, null);
-            Manager.ActiveDocument.PreviewLayer.Reset();
-        }
-
-        private void UseTool(List<Coordinates> mouseMoveCords, BitmapOperationTool tool, SKColor color)
-        {
-            if (sizeSetting == null)
-            {
-                sizeSetting = tool.Toolbar.GetSetting<SizeSetting>("ToolSize");
-            }
-
-            int thickness = sizeSetting != null ? sizeSetting.Value : 1;
-
-            bool shiftDown = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
-
-            if (shiftDown && tool.UsesShift)
-            {
-                bool mouseInLine = DoCoordsFormLine(mouseMoveCords, thickness);
-
-                if (!mouseInLine)
-                {
-                    mouseMoveCords = GetSquareCoordiantes(mouseMoveCords);
-                }
-                else
-                {
-                    mouseMoveCords = GetLineCoordinates(mouseMoveCords, thickness);
-                }
-            }
-
-            if (!tool.RequiresPreviewLayer)
-            {
-                if (!Manager.ActiveDocument.PreviewLayer.IsReset)
-                    Manager.ActiveDocument.PreviewLayer.Reset();
-                tool.Use(Manager.ActiveLayer, mouseMoveCords, color);
-                BitmapChanged?.Invoke(this, null);
-            }
-            else
-            {
-                UseToolOnPreviewLayer(mouseMoveCords, tool.ClearPreviewLayerOnEachIteration);
-            }
-        }
-
-        private bool DoCoordsFormLine(List<Coordinates> coords, int thickness)
-        {
-            var p1 = coords[0];
-            var p2 = coords[^1];
-            //find delta and mirror to first quadrant
-            float dX = Math.Abs(p2.X - p1.X);
-            float dY = Math.Abs(p2.Y - p1.Y);
-
-            //normalize
-            float length = (float)Math.Sqrt(dX * dX + dY * dY);
-            if (length == 0)
-                return false;
-            dX = dX / length;
-            dY = dY / length;
-
-            return dX < 0.25f || dY < 0.25f; //angle < 15 deg or angle > 75 deg (sin 15 ~= 0.25)
-        }
-
-        private List<Coordinates> GetLineCoordinates(List<Coordinates> mouseMoveCords, int thickness)
-        {
-            int y = mouseMoveCords[0].Y;
-            int x = mouseMoveCords[0].X;
-
-            if (Math.Abs(mouseMoveCords[^1].X - mouseMoveCords[0].X) > Math.Abs(mouseMoveCords[^1].Y - mouseMoveCords[0].Y))
-            {
-                y = mouseMoveCords[^1].Y;
-            }
-            else
-            {
-                x = mouseMoveCords[^1].X;
-            }
-
-            mouseMoveCords[0] = new Coordinates(x, y);
-            return mouseMoveCords;
-        }
-
-        /// <summary>
-        ///     Extracts square from rectangle mouse drag, used to draw symmetric shapes.
-        /// </summary>
-        private List<Coordinates> GetSquareCoordiantes(List<Coordinates> mouseMoveCords)
-        {
-            var p1 = mouseMoveCords[0];
-            var p2 = mouseMoveCords[^1];
-
-            //find delta and mirror to first quadrant
-            var dX = Math.Abs(p2.X - p1.X);
-            var dY = Math.Abs(p2.Y - p1.Y);
-
-            float sqrt2 = (float)Math.Sqrt(2);
-            //vector of length 1 at 45 degrees;
-            float diagX, diagY;
-            diagX = diagY = 1 / sqrt2;
-
-            //dot product of delta and diag, returns length of [delta projected onto diag]
-            float projectedLength = diagX * dX + diagY * dY;
-            //project above onto axes
-            float axisLength = projectedLength / sqrt2;
-
-            //final coords
-            float x = -Math.Sign(p2.X - p1.X) * axisLength;
-            float y = -Math.Sign(p2.Y - p1.Y) * axisLength;
-            mouseMoveCords[0] = new Coordinates((int)x + p2.X, (int)y + p2.Y);
-            return mouseMoveCords;
-        }
-
-        private BitmapPixelChanges GetOldPixelsValues(Coordinates[] coordinates)
-        {
-            Dictionary<Coordinates, SKColor> values = new Dictionary<Coordinates, SKColor>();
-            //using (Manager.ActiveLayer.LayerBitmap.GetBitmapContext(ReadWriteMode.ReadOnly))
-            {
-                Coordinates[] relativeCoords = Manager.ActiveLayer.ConvertToRelativeCoordinates(coordinates);
-                for (int i = 0; i < coordinates.Length; i++)
-                {
-                    var cl = Manager.ActiveLayer.GetPixel(relativeCoords[i].X, relativeCoords[i].Y);
-                    values.Add(
-                        coordinates[i],
-                        cl);
-                }
-            }
-
-            return new BitmapPixelChanges(values);
-        }
-
-        private void UseToolOnPreviewLayer(List<Coordinates> mouseMove, bool clearPreviewLayer = true)
-        {
-            if (mouseMove.Count > 0)
-            {
-                if (clearPreviewLayer)
-                {
-                    Manager.ActiveDocument.PreviewLayer.ClearCanvas();
-                }
-
-                ((BitmapOperationTool)Tools.ActiveTool).Use(
-                    Manager.ActiveDocument.PreviewLayer,
-                    mouseMove,
-                    Manager.PrimaryColor);
-            }
         }
         }
     }
     }
 }
 }

+ 15 - 0
PixiEditor/Models/Controllers/ICanvasInputTarget.cs

@@ -0,0 +1,15 @@
+using PixiEditor.Models.Tools;
+using System.Windows.Input;
+
+namespace PixiEditor.Models.Controllers
+{
+    public interface ICanvasInputTarget
+    {
+        void OnToolChange(Tool tool);
+        void OnKeyDown(Key key);
+        void OnKeyUp(Key key);
+        void OnLeftMouseButtonDown(double canvasPosX, double canvasPosY);
+        void OnLeftMouseButtonUp();
+        void OnMouseMove(double newCanvasX, double newCanvasY);
+    }
+}

+ 53 - 0
PixiEditor/Models/Controllers/MouseInputFilter.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace PixiEditor.Models.Controllers
+{
+    internal class MouseInputFilter
+    {
+        public EventHandler<MouseButton> OnMouseDown;
+        public EventHandler OnMouseMove;
+        public EventHandler<MouseButton> OnMouseUp;
+
+
+        private Dictionary<MouseButton, MouseButtonState> buttonStates = new()
+        {
+            [MouseButton.Left] = MouseButtonState.Released,
+            [MouseButton.Right] = MouseButtonState.Released,
+            [MouseButton.Middle] = MouseButtonState.Released,
+        };
+
+        public void MouseDown(object args) => MouseDown(((MouseButtonEventArgs)args).ChangedButton);
+        public void MouseDown(MouseButton button)
+        {
+            if (button is MouseButton.XButton1 or MouseButton.XButton2)
+                return;
+            if (buttonStates[button] == MouseButtonState.Pressed)
+                return;
+            buttonStates[button] = MouseButtonState.Pressed;
+
+            OnMouseDown?.Invoke(this, button);
+        }
+
+        public void MouseMove(object args) => OnMouseMove?.Invoke(this, EventArgs.Empty);
+        public void MouseMove(MouseEventArgs args) => OnMouseMove?.Invoke(this, EventArgs.Empty);
+
+        public void MouseUp(object args) => MouseUp(((MouseButtonEventArgs)args).ChangedButton);
+        public void MouseUp(object sender, Point p, MouseButton button) => MouseUp(button);
+        public void MouseUp(MouseButton button)
+        {
+            if (button is MouseButton.XButton1 or MouseButton.XButton2)
+                return;
+            if (buttonStates[button] == MouseButtonState.Released)
+                return;
+            buttonStates[button] = MouseButtonState.Released;
+
+            OnMouseUp?.Invoke(this, button);
+        }
+    }
+}

+ 0 - 94
PixiEditor/Models/Controllers/MouseMovementController.cs

@@ -1,94 +0,0 @@
-using PixiEditor.Models.Position;
-using System;
-using System.Collections.Generic;
-using System.Windows.Input;
-
-namespace PixiEditor.Models.Controllers
-{
-    public class MouseMovementController
-    {
-        public event EventHandler StartedRecordingChanges;
-
-        public event EventHandler<MouseEventArgs> OnMouseDown;
-        public event EventHandler<MouseMovementEventArgs> OnMouseDownCoordinates;
-
-        public event EventHandler<MouseEventArgs> OnMouseUp;
-
-        public event EventHandler<MouseMovementEventArgs> MousePositionChanged;
-
-        public event EventHandler StoppedRecordingChanges;
-
-        public MouseButtonState LeftMouseState { get; private set; }
-
-        public List<Coordinates> LastMouseMoveCoordinates { get; set; } = new List<Coordinates>();
-
-        public bool IsRecordingChanges { get; private set; }
-
-        public bool ClickedOnCanvas { get; set; }
-
-
-        public void StartRecordingMouseMovementChanges(bool clickedOnCanvas)
-        {
-            if (IsRecordingChanges == false)
-            {
-                LastMouseMoveCoordinates.Clear();
-                IsRecordingChanges = true;
-                ClickedOnCanvas = clickedOnCanvas;
-                StartedRecordingChanges?.Invoke(this, EventArgs.Empty);
-            }
-        }
-
-        public void RecordMouseMovementChange(Coordinates mouseCoordinates)
-        {
-            if (IsRecordingChanges)
-            {
-                if (LastMouseMoveCoordinates.Count == 0 || mouseCoordinates != LastMouseMoveCoordinates[^1])
-                {
-                    LastMouseMoveCoordinates.Insert(0, mouseCoordinates);
-                    MousePositionChanged?.Invoke(this, new MouseMovementEventArgs(mouseCoordinates));
-                }
-            }
-        }
-
-        /// <summary>
-        ///     Plain mouse move, does not affect mouse drag recordings.
-        /// </summary>
-        public void MouseMoved(Coordinates mouseCoordinates)
-        {
-            MousePositionChanged?.Invoke(this, new MouseMovementEventArgs(mouseCoordinates));
-        }
-
-        /// <summary>
-        /// Plain mouse down, does not affect mouse recordings.
-        /// </summary>
-        public void MouseDown(MouseEventArgs args)
-        {
-            LeftMouseState = args.LeftButton;
-            OnMouseDown?.Invoke(this, args);
-        }
-
-        public void MouseDownCoordinates(Coordinates mouseCoordinates)
-        {
-            OnMouseDownCoordinates?.Invoke(this, new MouseMovementEventArgs(mouseCoordinates));
-        }
-
-        /// <summary>
-        /// Plain mouse up, does not affect mouse recordings.
-        /// </summary>
-        public void MouseUp(MouseEventArgs args)
-        {
-            LeftMouseState = MouseButtonState.Released;
-            OnMouseUp?.Invoke(this, args);
-        }
-
-        public void StopRecordingMouseMovementChanges()
-        {
-            if (IsRecordingChanges)
-            {
-                IsRecordingChanges = false;
-                ClickedOnCanvas = false;
-                StoppedRecordingChanges?.Invoke(this, EventArgs.Empty);
-            }
-        }
-    }
-}

+ 0 - 14
PixiEditor/Models/Controllers/ReadonlyToolUtility.cs

@@ -1,14 +0,0 @@
-using System.Collections.Generic;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools;
-
-namespace PixiEditor.Models.Controllers
-{
-    public class ReadonlyToolUtility
-    {
-        public void ExecuteTool(List<Coordinates> mouseMove, ReadonlyTool tool)
-        {
-            tool.Use(mouseMove);
-        }
-    }
-}

+ 97 - 0
PixiEditor/Models/Controllers/ToolSession.cs

@@ -0,0 +1,97 @@
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using System;
+using System.Collections.Generic;
+using System.Windows.Input;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class ToolSession
+    {
+        private List<Coordinates> mouseMovement = new();
+        private bool ended = false;
+
+        public IReadOnlyList<Coordinates> MouseMovement => mouseMovement;
+        public Tool Tool { get; }
+
+        public bool IsCtrlDown { get; private set; }
+        public bool IsShiftDown { get; private set; }
+        public bool IsAltDown { get; private set; }
+
+        public ToolSession(
+            Tool tool,
+            double mouseXOnCanvas,
+            double mouseYOnCanvas,
+            IReadOnlyDictionary<Key, KeyStates> keyboardStates)
+        {
+            if (tool == null)
+                throw new ArgumentNullException(nameof(tool));
+            Tool = tool;
+
+            Tool.Session = this;
+            InvokeKeyboardEvents(keyboardStates);
+            mouseMovement.Add(new((int)Math.Floor(mouseXOnCanvas), (int)Math.Floor(mouseYOnCanvas)));
+            Tool.BeforeUse();
+        }
+
+        private void InvokeKeyboardEvents(IReadOnlyDictionary<Key, KeyStates> keyboardStates)
+        {
+            foreach (var pair in keyboardStates)
+            {
+                if (pair.Value == KeyStates.None)
+                    OnKeyUp(pair.Key);
+                else if (pair.Value == KeyStates.Down)
+                    OnKeyDown(pair.Key);
+            }
+        }
+
+        public void EndSession(IReadOnlyDictionary<Key, KeyStates> keyboardStates)
+        {
+            if (ended)
+                throw new Exception("Session has ended already");
+            ended = true;
+
+            Tool.AfterUse();
+            InvokeReleaseKeyboardEvents(keyboardStates);
+            Tool.Session = null;
+        }
+
+        private void InvokeReleaseKeyboardEvents(IReadOnlyDictionary<Key, KeyStates> keyboardStates)
+        {
+            foreach (var pair in keyboardStates)
+            {
+                if (pair.Value == KeyStates.Down)
+                    OnKeyUp(pair.Key);
+            }
+        }
+
+        public void OnKeyDown(Key key)
+        {
+            if (key == Key.LeftCtrl)
+                IsCtrlDown = true;
+            else if (key == Key.LeftShift)
+                IsShiftDown = true;
+            else if (key == Key.LeftAlt)
+                IsAltDown = true;
+
+            Tool.OnKeyDown(key);
+        }
+
+        public void OnKeyUp(Key key)
+        {
+            if (key == Key.LeftCtrl)
+                IsCtrlDown = false;
+            else if (key == Key.LeftShift)
+                IsShiftDown = false;
+            else if (key == Key.LeftAlt)
+                IsAltDown = false;
+
+            Tool.OnKeyUp(key);
+        }
+
+        public void OnPixelPositionChange(Coordinates pos)
+        {
+            mouseMovement.Add(pos);
+        }
+    }
+}

+ 140 - 0
PixiEditor/Models/Controllers/ToolSessionController.cs

@@ -0,0 +1,140 @@
+using PixiEditor.Models.Position;
+using PixiEditor.Models.Tools;
+using System;
+using System.Collections.Generic;
+using System.Windows.Input;
+
+namespace PixiEditor.Models.Controllers
+{
+    public class ToolSessionController : ICanvasInputTarget
+    {
+        public event EventHandler<MouseMovementEventArgs> PixelMousePositionChanged;
+        public event EventHandler<(double, double)> PreciseMousePositionChanged;
+        public event EventHandler<(Key, KeyStates)> KeyStateChanged;
+
+        public event EventHandler<ToolSession> SessionStarted;
+        public event EventHandler<ToolSession> SessionEnded;
+
+        public MouseButtonState LeftMouseState { get; private set; }
+
+        public bool IsShiftDown => keyboardState.ContainsKey(Key.LeftShift) ? keyboardState[Key.LeftShift] == KeyStates.Down : false;
+        public bool IsCtrlDown => keyboardState.ContainsKey(Key.LeftCtrl) ? keyboardState[Key.LeftCtrl] == KeyStates.Down : false;
+        public bool IsAltDown => keyboardState.ContainsKey(Key.LeftAlt) ? keyboardState[Key.LeftAlt] == KeyStates.Down : false;
+
+        public Coordinates LastPixelPosition => new(lastPixelX, lastPixelY);
+
+        private int lastPixelX;
+        private int lastPixelY;
+
+        private Dictionary<Key, KeyStates> keyboardState = new();
+        private Tool currentTool = null;
+        private ToolSession currentSession = null;
+
+        private void TryStartToolSession(Tool tool, double mouseXOnCanvas, double mouseYOnCanvas)
+        {
+            if (currentSession != null)
+                return;
+            currentSession = new(tool, mouseXOnCanvas, mouseYOnCanvas, keyboardState);
+            SessionStarted?.Invoke(this, currentSession);
+        }
+
+        private void TryStopToolSession()
+        {
+            if (currentSession == null)
+                return;
+            currentSession.EndSession(keyboardState);
+            SessionEnded?.Invoke(this, currentSession);
+            currentSession = null;
+        }
+
+        public void OnKeyDown(Key key)
+        {
+            key = ConvertRightKeys(key);
+            UpdateKeyState(key, KeyStates.Down);
+            currentSession?.OnKeyDown(key);
+            KeyStateChanged?.Invoke(this, (key, KeyStates.Down));
+        }
+
+        public void OnKeyUp(Key key)
+        {
+            key = ConvertRightKeys(key);
+            UpdateKeyState(key, KeyStates.None);
+            currentSession?.OnKeyUp(key);
+            KeyStateChanged?.Invoke(this, (key, KeyStates.None));
+        }
+
+        private void UpdateKeyState(Key key, KeyStates state)
+        {
+            key = ConvertRightKeys(key);
+            if (!keyboardState.ContainsKey(key))
+                keyboardState.Add(key, state);
+            else
+                keyboardState[key] = state;
+        }
+
+        private Key ConvertRightKeys(Key key)
+        {
+            if (key == Key.RightAlt)
+                return Key.LeftAlt;
+            if (key == Key.RightCtrl)
+                return Key.LeftCtrl;
+            if (key == Key.RightShift)
+                return Key.LeftShift;
+            return key;
+        }
+
+        public void ForceStopActiveSessionIfAny() => TryStopToolSession();
+
+        public void OnToolChange(Tool tool)
+        {
+            currentTool = tool;
+            TryStopToolSession();
+        }
+
+        public void OnMouseMove(double newCanvasX, double newCanvasY)
+        {
+            //update internal state
+
+            int newX = (int)Math.Floor(newCanvasX);
+            int newY = (int)Math.Floor(newCanvasY);
+            bool pixelPosChanged = false;
+            if (lastPixelX != newX || lastPixelY != newY)
+            {
+                lastPixelX = newX;
+                lastPixelY = newY;
+                pixelPosChanged = true;
+            }
+
+
+            //call session events
+            if (currentSession != null && pixelPosChanged)
+                currentSession.OnPixelPositionChange(new(newX, newY));
+
+            //call internal events
+            PreciseMousePositionChanged?.Invoke(this, (newCanvasX, newCanvasY));
+            if (pixelPosChanged)
+                PixelMousePositionChanged?.Invoke(this, new MouseMovementEventArgs(new Coordinates(newX, newY)));
+        }
+
+        public void OnLeftMouseButtonDown(double canvasPosX, double canvasPosY)
+        {
+            //update internal state
+            LeftMouseState = MouseButtonState.Pressed;
+
+            //call session events
+
+            if (currentTool == null)
+                throw new Exception("Current tool must not be null here");
+            TryStartToolSession(currentTool, canvasPosX, canvasPosY);
+        }
+
+        public void OnLeftMouseButtonUp()
+        {
+            //update internal state
+            LeftMouseState = MouseButtonState.Released;
+
+            //call session events
+            TryStopToolSession();
+        }
+    }
+}

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

@@ -195,8 +195,6 @@ namespace PixiEditor.Models.DataHolders
 
 
         private void SetAsActiveOnClick(object obj)
         private void SetAsActiveOnClick(object obj)
         {
         {
-            XamlAccesibleViewModel.BitmapManager.MouseController.StopRecordingMouseMovementChanges();
-            //XamlAccesibleViewModel.BitmapManager.MouseController.StartRecordingMouseMovementChanges(true);
             if (XamlAccesibleViewModel.BitmapManager.ActiveDocument != this)
             if (XamlAccesibleViewModel.BitmapManager.ActiveDocument != this)
             {
             {
                 XamlAccesibleViewModel.BitmapManager.ActiveDocument = this;
                 XamlAccesibleViewModel.BitmapManager.ActiveDocument = this;

+ 16 - 20
PixiEditor/Models/Tools/BitmapOperationTool.cs

@@ -1,13 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-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.Undo;
 using PixiEditor.Models.Undo;
 using SkiaSharp;
 using SkiaSharp;
+using System.Collections.Generic;
 
 
 namespace PixiEditor.Models.Tools
 namespace PixiEditor.Models.Tools
 {
 {
@@ -18,33 +14,33 @@ namespace PixiEditor.Models.Tools
         public bool ClearPreviewLayerOnEachIteration { get; set; } = true;
         public bool ClearPreviewLayerOnEachIteration { get; set; } = true;
 
 
         public bool UseDefaultUndoMethod { get; set; } = true;
         public bool UseDefaultUndoMethod { get; set; } = true;
-        public virtual bool UsesShift => true;
 
 
         private StorageBasedChange _change;
         private StorageBasedChange _change;
 
 
-        public abstract void Use(Layer layer, List<Coordinates> mouseMove, SKColor color);
+        public abstract void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color);
+
+        public override void BeforeUse()
+        {
+            if (UseDefaultUndoMethod)
+            {
+                Document doc = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
+                _change = new StorageBasedChange(doc, new[] { doc.ActiveLayer }, true);
+            }
+        }
 
 
         /// <summary>
         /// <summary>
         /// Executes undo adding procedure.
         /// Executes undo adding procedure.
         /// </summary>
         /// </summary>
         /// <param name="document">Active document</param>
         /// <param name="document">Active document</param>
         /// <remarks>When overriding, set UseDefaultUndoMethod to false.</remarks>
         /// <remarks>When overriding, set UseDefaultUndoMethod to false.</remarks>
-        public override void AddUndoProcess(Document document)
+        public override void AfterUse()
         {
         {
-            if (!UseDefaultUndoMethod) return;
-
+            if (!UseDefaultUndoMethod)
+                return;
+            var document = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
             var args = new object[] { _change.Document };
             var args = new object[] { _change.Document };
             document.UndoManager.AddUndoChange(_change.ToChange(StorageBasedChange.BasicUndoProcess, args));
             document.UndoManager.AddUndoChange(_change.ToChange(StorageBasedChange.BasicUndoProcess, args));
             _change = null;
             _change = null;
         }
         }
-
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
-            if (UseDefaultUndoMethod && e.LeftButton == MouseButtonState.Pressed)
-            {
-                Document doc = ViewModels.ViewModelMain.Current.BitmapManager.ActiveDocument;
-                _change = new StorageBasedChange(doc, new[] { doc.ActiveLayer }, true);
-            }
-        }
     }
     }
 }
 }

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

@@ -1,10 +1,10 @@
-using System.Collections.Generic;
-using PixiEditor.Models.Position;
+using PixiEditor.Models.Position;
+using System.Collections.Generic;
 
 
 namespace PixiEditor.Models.Tools
 namespace PixiEditor.Models.Tools
 {
 {
     public abstract class ReadonlyTool : Tool
     public abstract class ReadonlyTool : Tool
     {
     {
-        public abstract void Use(List<Coordinates> pixels);
+        public abstract void Use(IReadOnlyList<Coordinates> pixels);
     }
     }
-}
+}

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

@@ -1,10 +1,8 @@
-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.Toolbars;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using SkiaSharp;
 using SkiaSharp;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
 using System.Windows.Input;
 using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools
 namespace PixiEditor.Models.Tools
@@ -55,9 +53,6 @@ namespace PixiEditor.Models.Tools
             Toolbar = new BasicShapeToolbar();
             Toolbar = new BasicShapeToolbar();
         }
         }
 
 
-        // TODO: Add cache for lines 31, 32 (hopefully it would speed up calculation)
-        public abstract override void Use(Layer layer, List<Coordinates> coordinates, SKColor color);
-
         public static void ThickenShape(Layer layer, SKColor color, IEnumerable<Coordinates> shape, int thickness)
         public static void ThickenShape(Layer layer, SKColor color, IEnumerable<Coordinates> shape, int thickness)
         {
         {
             foreach (Coordinates item in shape)
             foreach (Coordinates item in shape)

+ 16 - 55
PixiEditor/Models/Tools/Tool.cs

@@ -1,19 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows;
-using System.Windows.Input;
-using PixiEditor.Helpers;
+using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Layers;
-using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.ToolSettings;
-using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using PixiEditor.Models.Undo;
-
-namespace PixiEditor.Models.Tools
+using PixiEditor.Models.Tools.ToolSettings;
+using PixiEditor.Models.Tools.ToolSettings.Toolbars;
+using System.Windows.Input;
+
+namespace PixiEditor.Models.Tools
 {
 {
     public abstract class Tool : NotifyableObject
     public abstract class Tool : NotifyableObject
     {
     {
@@ -25,6 +17,8 @@ namespace PixiEditor.Models.Tools
 
 
         public virtual bool HideHighlight { get; }
         public virtual bool HideHighlight { get; }
 
 
+        public virtual bool RequiresPreciseMouseData { get; }
+
         public abstract string Tooltip { get; }
         public abstract string Tooltip { get; }
 
 
         public string ActionDisplay
         public string ActionDisplay
@@ -51,53 +45,20 @@ namespace PixiEditor.Models.Tools
 
 
         public Toolbar Toolbar { get; set; } = new EmptyToolbar();
         public Toolbar Toolbar { get; set; } = new EmptyToolbar();
 
 
-        public bool CanStartOutsideCanvas { get; set; } = false;
+        public ToolSession Session { get; set; }
 
 
         private bool isActive;
         private bool isActive;
         private string actionDisplay = string.Empty;
         private string actionDisplay = string.Empty;
 
 
-        public virtual void OnMouseDown(MouseEventArgs e)
-        {
-        }
 
 
-        public virtual void AddUndoProcess(Document document)
-        {
-        }
+        public virtual void OnKeyDown(Key key) { }
 
 
-        public virtual void OnMouseUp(MouseEventArgs e)
-        {
-        }
+        public virtual void OnKeyUp(Key key) { }
 
 
-        public virtual void OnKeyDown(KeyEventArgs e)
-        {
-        }
+        public virtual void BeforeUse() { }
 
 
-        public virtual void OnKeyUp(KeyEventArgs e)
-        {
-        }
-
-        public virtual void OnStart(Coordinates clickPosition)
-        {
-        }
+        public virtual void AfterUse() { }
 
 
-        public virtual void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
-        }
-
-        public virtual void OnStoppedRecordingMouseUp(MouseEventArgs e)
-        {
-        }
-
-        public virtual void OnMouseMove(MouseEventArgs e)
-        {
-        }
-
-        public virtual void OnSelected()
-        {
-        }
-
-        public virtual void OnDeselected()
-        {
-        }
-    }
-}
+        public virtual void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown) { }
+    }
+}

+ 17 - 31
PixiEditor/Models/Tools/Tools/BrightnessTool.cs

@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Windows.Input;
-using System.Linq;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
+using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Colors;
 using PixiEditor.Models.Colors;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
@@ -12,8 +7,9 @@ using PixiEditor.Models.Position;
 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 SkiaSharp;
 using SkiaSharp;
+using System;
+using System.Collections.Generic;
 using System.Windows;
 using System.Windows;
-using PixiEditor.Helpers;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
@@ -21,62 +17,52 @@ namespace PixiEditor.Models.Tools.Tools
     {
     {
         private const float CorrectionFactor = 5f; // Initial correction factor
         private const float CorrectionFactor = 5f; // Initial correction factor
 
 
+        private readonly string defaultActionDisplay = "Draw on pixels to make them brighter. Hold Ctrl to darken.";
         private readonly List<Coordinates> pixelsVisited = new List<Coordinates>();
         private readonly List<Coordinates> pixelsVisited = new List<Coordinates>();
         private List<DoubleCoords> circleCache = new List<DoubleCoords>();
         private List<DoubleCoords> circleCache = new List<DoubleCoords>();
         private int cachedCircleSize = -1;
         private int cachedCircleSize = -1;
 
 
-        private string defaultActionDisplay = "Draw on pixels to make them brighter. Hold Ctrl to darken.";
         public BrightnessTool()
         public BrightnessTool()
         {
         {
             ActionDisplay = defaultActionDisplay;
             ActionDisplay = defaultActionDisplay;
             Toolbar = new BrightnessToolToolbar(CorrectionFactor);
             Toolbar = new BrightnessToolToolbar(CorrectionFactor);
         }
         }
 
 
-        public override bool UsesShift => false;
-
         public override string Tooltip => "Makes pixels brighter or darker (U). Hold Ctrl to make pixels darker.";
         public override string Tooltip => "Makes pixels brighter or darker (U). Hold Ctrl to make pixels darker.";
 
 
         public BrightnessMode Mode { get; set; } = BrightnessMode.Default;
         public BrightnessMode Mode { get; set; } = BrightnessMode.Default;
 
 
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
-        {
-            base.OnRecordingLeftMouseDown(e);
-            pixelsVisited.Clear();
-        }
-
-        public override void OnKeyDown(KeyEventArgs e)
+        public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         {
         {
-            if (e.Key is Key.LeftCtrl or Key.RightCtrl)
-            {
+            if (!ctrlIsDown)
+                ActionDisplay = defaultActionDisplay;
+            else
                 ActionDisplay = "Draw on pixels to make them darker. Release Ctrl to brighten.";
                 ActionDisplay = "Draw on pixels to make them darker. Release Ctrl to brighten.";
-            }
         }
         }
 
 
-        public override void OnKeyUp(KeyEventArgs e)
+        public override void BeforeUse()
         {
         {
-            if (e.Key is Key.LeftCtrl or Key.RightCtrl)
-            {
-                ActionDisplay = defaultActionDisplay;
-            }
+            base.BeforeUse();
+            pixelsVisited.Clear();
         }
         }
 
 
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
             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;
             Mode = Toolbar.GetEnumSetting<BrightnessMode>("BrightnessMode").Value;
             Mode = Toolbar.GetEnumSetting<BrightnessMode>("BrightnessMode").Value;
 
 
-            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+            if (Session.IsCtrlDown)
             {
             {
-                ChangeBrightness(layer, coordinates[0], toolSize, -correctionFactor);
+                ChangeBrightness(activeLayer, recordedMouseMovement[^1], toolSize, -correctionFactor);
             }
             }
             else
             else
             {
             {
-                ChangeBrightness(layer, coordinates[0], toolSize, correctionFactor);
+                ChangeBrightness(activeLayer, recordedMouseMovement[^1], toolSize, correctionFactor);
             }
             }
         }
         }
 
 
-        public void ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
+        private void ChangeBrightness(Layer layer, Coordinates coordinates, int toolSize, float correctionFactor)
         {
         {
             if (cachedCircleSize != toolSize)
             if (cachedCircleSize != toolSize)
                 UpdateCircleCache(toolSize);
                 UpdateCircleCache(toolSize);
@@ -112,7 +98,7 @@ namespace PixiEditor.Models.Tools.Tools
             layer.InvokeLayerBitmapChange(dirtyRect);
             layer.InvokeLayerBitmapChange(dirtyRect);
         }
         }
 
 
-        public void UpdateCircleCache(int newCircleSize)
+        private void UpdateCircleCache(int newCircleSize)
         {
         {
             cachedCircleSize = newCircleSize;
             cachedCircleSize = newCircleSize;
             DoubleCoords rect = CoordinatesCalculator.CalculateThicknessCenter(new Coordinates(0, 0), newCircleSize);
             DoubleCoords rect = CoordinatesCalculator.CalculateThicknessCenter(new Coordinates(0, 0), newCircleSize);

+ 30 - 37
PixiEditor/Models/Tools/Tools/CircleTool.cs

@@ -5,16 +5,44 @@ using PixiEditor.Models.Tools.ToolSettings.Settings;
 using SkiaSharp;
 using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
-using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class CircleTool : ShapeTool
     public class CircleTool : ShapeTool
     {
     {
+        private string defaultActionDisplay = "Click and move mouse to draw a circle. Hold Shift to draw an even one.";
+
+        public CircleTool()
+        {
+            ActionDisplay = defaultActionDisplay;
+        }
+
+        public override string Tooltip => "Draws circle on canvas (C). Hold Shift to draw even circle.";
+
+        public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
+        {
+            if (shiftIsDown)
+                ActionDisplay = "Click and move mouse to draw an even circle.";
+            else
+                ActionDisplay = defaultActionDisplay;
+        }
+
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
+        {
+            int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
+            var hasFillColor = Toolbar.GetSetting<BoolSetting>("Fill").Value;
+            Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
+            SKColor fill = new SKColor(temp.R, temp.G, temp.B, temp.A);
+
+            var (start, end) = Session.IsShiftDown ?
+                CoordinatesHelper.GetSquareCoordiantes(recordedMouseMovement) :
+                (recordedMouseMovement[0], recordedMouseMovement[^1]);
+
+            DrawEllipseFromCoordinates(previewLayer, start, end, color, fill, thickness, hasFillColor);
+        }
 
 
         public static void DrawEllipseFromCoordinates(Layer layer, Coordinates first, Coordinates second,
         public static void DrawEllipseFromCoordinates(Layer layer, Coordinates first, Coordinates second,
             SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
             SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
@@ -92,40 +120,5 @@ namespace PixiEditor.Models.Tools.Tools
                 }
                 }
             }
             }
         }
         }
-
-        private string defaultActionDisplay = "Click and move mouse to draw a circle. Hold Shift to draw an even one.";
-
-        public CircleTool()
-        {
-            ActionDisplay = defaultActionDisplay;
-        }
-
-        public override string Tooltip => "Draws circle on canvas (C). Hold Shift to draw even circle.";
-
-        public override void OnKeyDown(KeyEventArgs e)
-        {
-            if (e.Key is Key.LeftShift or Key.RightShift)
-            {
-                ActionDisplay = "Click and move mouse to draw an even circle.";
-            }
-        }
-
-        public override void OnKeyUp(KeyEventArgs e)
-        {
-            if (e.Key is Key.LeftShift or Key.RightShift)
-            {
-                ActionDisplay = defaultActionDisplay;
-            }
-        }
-
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
-        {
-            int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
-            var hasFillColor = Toolbar.GetSetting<BoolSetting>("Fill").Value;
-            Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
-            SKColor fill = new SKColor(temp.R, temp.G, temp.B, temp.A);
-            DrawEllipseFromCoordinates(layer, coordinates[^1], coordinates[0], color, fill, thickness, hasFillColor);
-        }
-
     }
     }
 }
 }

+ 10 - 32
PixiEditor/Models/Tools/Tools/ColorPickerTool.cs

@@ -8,13 +8,10 @@ using PixiEditor.ViewModels;
 using SkiaSharp;
 using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
-using System.Windows.Input;
-using static System.Math;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class ColorPickerTool : ReadonlyTool
+    internal class ColorPickerTool : ReadonlyTool
     {
     {
         private readonly DocumentProvider _docProvider;
         private readonly DocumentProvider _docProvider;
         private readonly BitmapManager _bitmapManager;
         private readonly BitmapManager _bitmapManager;
@@ -29,11 +26,13 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public override bool HideHighlight => true;
         public override bool HideHighlight => true;
 
 
+        public override bool RequiresPreciseMouseData => true;
+
         public override string Tooltip => "Picks the primary color from the canvas. (O)";
         public override string Tooltip => "Picks the primary color from the canvas. (O)";
 
 
-        public override void Use(List<Coordinates> coordinates)
+        public override void Use(IReadOnlyList<Coordinates> recordedMouseMovement)
         {
         {
-            var coords = coordinates.First();
+            var coords = recordedMouseMovement[^1];
             var doc = _docProvider.GetDocument();
             var doc = _docProvider.GetDocument();
             if (coords.X < 0 || coords.Y < 0 || coords.X >= doc.Width || coords.Y >= doc.Height)
             if (coords.X < 0 || coords.Y < 0 || coords.X >= doc.Width || coords.Y >= doc.Height)
                 return;
                 return;
@@ -45,12 +44,12 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             Layer referenceLayer = _docProvider.GetReferenceLayer();
             Layer referenceLayer = _docProvider.GetReferenceLayer();
 
 
-            if (referenceLayer != null && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
+            if (referenceLayer != null && Session.IsCtrlDown)
             {
             {
                 double preciseX = _docProvider.GetDocument().MouseXOnCanvas;
                 double preciseX = _docProvider.GetDocument().MouseXOnCanvas;
                 double preciseY = _docProvider.GetDocument().MouseYOnCanvas;
                 double preciseY = _docProvider.GetDocument().MouseYOnCanvas;
 
 
-                if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)))
+                if (Session.IsAltDown)
                 {
                 {
                     return GetCombinedColor(x, y, preciseX, preciseY);
                     return GetCombinedColor(x, y, preciseX, preciseY);
                 }
                 }
@@ -112,32 +111,11 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
         }
         }
 
 
-        public override void OnKeyDown(KeyEventArgs e)
-        {
-            UpdateActionDisplay();
-        }
-
-        public override void OnKeyUp(KeyEventArgs e)
-        {
-            UpdateActionDisplay();
-        }
-
-        public override void OnSelected()
-        {
-            UpdateActionDisplay();
-        }
-
-        public override void OnDeselected()
-        {
-            _bitmapManager.OnlyReferenceLayer = false;
-            _bitmapManager.HideReferenceLayer = false;
-        }
-
-        private void UpdateActionDisplay()
+        public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         {
         {
-            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+            if (ctrlIsDown)
             {
             {
-                if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
+                if (altIsDown)
                 {
                 {
                     _bitmapManager.HideReferenceLayer = false;
                     _bitmapManager.HideReferenceLayer = false;
                     _bitmapManager.OnlyReferenceLayer = false;
                     _bitmapManager.OnlyReferenceLayer = false;

+ 6 - 9
PixiEditor/Models/Tools/Tools/EraserTool.cs

@@ -8,7 +8,7 @@ using System.Collections.Generic;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class EraserTool : BitmapOperationTool
+    internal class EraserTool : BitmapOperationTool
     {
     {
         private readonly PenTool pen;
         private readonly PenTool pen;
 
 
@@ -18,20 +18,17 @@ namespace PixiEditor.Models.Tools.Tools
             Toolbar = new BasicToolbar();
             Toolbar = new BasicToolbar();
             pen = new PenTool(bitmapManager);
             pen = new PenTool(bitmapManager);
         }

         }

-

-        public override bool UsesShift => false;

-

         public override string Tooltip => "Erasers color from pixel. (E)";
         public override string Tooltip => "Erasers color from pixel. (E)";
 
 
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
-            Erase(layer, coordinates, Toolbar.GetSetting<SizeSetting>("ToolSize").Value);
+            Erase(activeLayer, recordedMouseMovement, Toolbar.GetSetting<SizeSetting>("ToolSize").Value);
         }
         }
 
 
-        public void Erase(Layer layer, List<Coordinates> coordinates, int toolSize)
+        public void Erase(Layer layer, IReadOnlyList<Coordinates> coordinates, int toolSize)
         {
         {
-            Coordinates startingCords = coordinates.Count > 1 ? coordinates[1] : coordinates[0];
-            pen.Draw(layer, startingCords, coordinates[0], SKColors.Transparent, toolSize, false, null, SKBlendMode.Src);
+            Coordinates startingCords = coordinates.Count > 1 ? coordinates[^2] : coordinates[0];
+            pen.Draw(layer, startingCords, coordinates[^1], SKColors.Transparent, toolSize, false, null, SKBlendMode.Src);
         }
         }
     }
     }
 }
 }

+ 7 - 8
PixiEditor/Models/Tools/Tools/FloodFillTool.cs

@@ -4,12 +4,11 @@ using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using SkiaSharp;
 using SkiaSharp;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Windows;
 using System.Windows;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class FloodFillTool : BitmapOperationTool
+    internal class FloodFillTool : BitmapOperationTool
     {
     {
         private BitmapManager BitmapManager { get; }
         private BitmapManager BitmapManager { get; }
         private SKPaint fillPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
         private SKPaint fillPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
@@ -22,17 +21,17 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public override string Tooltip => "Fills area with color. (G)";
         public override string Tooltip => "Fills area with color. (G)";
 
 
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
-            if (layer.IsReset)
+            if (activeLayer.IsReset)
             {
             {
-                layer.DynamicResizeAbsolute(new(0, 0, BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
-                layer.LayerBitmap.SkiaSurface.Canvas.Clear(color);
-                layer.InvokeLayerBitmapChange();
+                activeLayer.DynamicResizeAbsolute(new(0, 0, BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
+                activeLayer.LayerBitmap.SkiaSurface.Canvas.Clear(color);
+                activeLayer.InvokeLayerBitmapChange();
             }
             }
             else
             else
             {
             {
-                LinearFill(layer, coordinates[0], color);
+                LinearFill(activeLayer, recordedMouseMovement[^1], color);
             }
             }
         }
         }
 
 

+ 91 - 97
PixiEditor/Models/Tools/Tools/LineTool.cs

@@ -1,4 +1,5 @@
-using PixiEditor.Models.Layers;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
@@ -6,35 +7,96 @@ using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows;
 using System.Windows;
-using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
     public class LineTool : ShapeTool
     public class LineTool : ShapeTool
     {
     {
-        private readonly CircleTool circleTool;
         private List<Coordinates> linePoints = new List<Coordinates>();
         private List<Coordinates> linePoints = new List<Coordinates>();
         private SKPaint paint = new SKPaint() { Style = SKPaintStyle.Stroke };
         private SKPaint paint = new SKPaint() { Style = SKPaintStyle.Stroke };
 
 
         public bool AutomaticallyResizeCanvas { get; set; } = true;
         public bool AutomaticallyResizeCanvas { get; set; } = true;
 
 
-        public static List<Coordinates> GetBresenhamLine(Coordinates start, Coordinates end)
+        private string defaltActionDisplay = "Click and move to draw a line. Hold Shift to draw an even one.";
+
+        public LineTool()
         {
         {
-            List<Coordinates> output = new List<Coordinates>();
-            CalculateBresenhamLine(start, end, output);
-            return output;
+            ActionDisplay = defaltActionDisplay;
+            Toolbar = new BasicToolbar();
         }
         }
 
 
-        public static void CalculateBresenhamLine(Coordinates start, Coordinates end, List<Coordinates> output)
+        public override string Tooltip => "Draws line on canvas (L). Hold Shift to draw even line.";
+
+        public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         {
         {
-            int x1 = start.X;
-            int x2 = end.X;
-            int y1 = start.Y;
-            int y2 = end.Y;
+            if (shiftIsDown)
+                ActionDisplay = "Click and move mouse to draw an even line.";
+            else
+                ActionDisplay = defaltActionDisplay;
+        }
+
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
+        {
+            int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
+
+            Coordinates start = recordedMouseMovement[0];
+            Coordinates end = recordedMouseMovement[^1];
+
+            if (Session.IsShiftDown)
+                (start, end) = CoordinatesHelper.GetSquareOrLineCoordinates(recordedMouseMovement);
+
+            DrawLine(previewLayer, start, end, color, thickness, SKBlendMode.Src);
+        }
 
 
+        public void DrawLine(
+            Layer layer, Coordinates start, Coordinates end, SKColor color, int thickness, SKBlendMode blendMode,
+            SKStrokeCap strokeCap = SKStrokeCap.Butt)
+        {
+            int x = start.X;
+            int y = start.Y;
+            int x1 = end.X;
+            int y1 = end.Y;
+
+            int dirtyX = Math.Min(x, x1) - thickness;
+            int dirtyY = Math.Min(y, y1) - thickness;
+
+            Int32Rect dirtyRect = new Int32Rect(
+                dirtyX,
+                dirtyY,
+                Math.Max(x1, x) + thickness - dirtyX,
+                Math.Max(y1, y) + thickness - dirtyY);
+            if (AutomaticallyResizeCanvas)
+            {
+                layer.DynamicResizeAbsolute(dirtyRect);
+            }
+
+            x -= layer.OffsetX;
+            y -= layer.OffsetY;
+            x1 -= layer.OffsetX;
+            y1 -= layer.OffsetY;
+
+            paint.StrokeWidth = thickness;
+            paint.Color = color;
+            paint.BlendMode = blendMode;
+            paint.StrokeCap = strokeCap;
+
+            if (thickness == 1)
+            {
+                DrawBresenhamLine(layer, x, y, x1, y1, paint);
+            }
+            else
+            {
+                layer.LayerBitmap.SkiaSurface.Canvas.DrawLine(x, y, x1, y1, paint);
+            }
+
+            layer.InvokeLayerBitmapChange(dirtyRect);
+        }
+
+        private void DrawBresenhamLine(Layer layer, int x1, int y1, int x2, int y2, SKPaint paint)
+        {
             if (x1 == x2 && y1 == y2)
             if (x1 == x2 && y1 == y2)
             {
             {
-                output.Add(start);
+                layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x1, y1, paint);
                 return;
                 return;
             }
             }
 
 
@@ -63,7 +125,7 @@ namespace PixiEditor.Models.Tools.Tools
                 dy = y1 - y2;
                 dy = y1 - y2;
             }
             }
 
 
-            output.Add(new Coordinates(x, y));
+            layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x, y, paint);
 
 
             if (dx > dy)
             if (dx > dy)
             {
             {
@@ -85,7 +147,7 @@ namespace PixiEditor.Models.Tools.Tools
                         x += xi;
                         x += xi;
                     }
                     }
 
 
-                    output.Add(new Coordinates(x, y));
+                    layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x, y, paint);
                 }
                 }
             }
             }
             else
             else
@@ -108,97 +170,29 @@ namespace PixiEditor.Models.Tools.Tools
                         y += yi;
                         y += yi;
                     }
                     }
 
 
-                    output.Add(new Coordinates(x, y));
+                    layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x, y, paint);
                 }
                 }
             }
             }
         }
         }
 
 
-        private string defaltActionDisplay = "Click and move to draw a line. Hold Shift to draw an even one.";
-
-        public LineTool()
-        {
-            ActionDisplay = defaltActionDisplay;
-            Toolbar = new BasicToolbar();
-            circleTool = new CircleTool();
-        }
-
-        public override string Tooltip => "Draws line on canvas (L). Hold Shift to draw even line.";
-
-        public override void OnKeyDown(KeyEventArgs e)
-        {
-            if (e.Key is Key.LeftShift or Key.RightShift)
-            {
-                ActionDisplay = "Click and move mouse to draw an even line.";
-            }
-        }
-
-        public override void OnKeyUp(KeyEventArgs e)
-        {
-            if (e.Key is Key.LeftShift or Key.RightShift)
-            {
-                ActionDisplay = defaltActionDisplay;
-            }
-        }
 
 
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public static List<Coordinates> GetBresenhamLine(Coordinates start, Coordinates end)
         {
         {
-            int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
-
-            Coordinates start = coordinates[0];
-            Coordinates end = coordinates[^1];
-
-            DrawLine(layer, start, end, color, thickness, SKBlendMode.Src);
+            List<Coordinates> output = new List<Coordinates>();
+            CalculateBresenhamLine(start, end, output);
+            return output;
         }
         }
 
 
-        public void DrawLine(
-            Layer layer, Coordinates start, Coordinates end, SKColor color, int thickness, SKBlendMode blendMode,
-            SKStrokeCap strokeCap = SKStrokeCap.Butt)
+        public static void CalculateBresenhamLine(Coordinates start, Coordinates end, List<Coordinates> output)
         {
         {
-            int x = start.X;
-            int y = start.Y;
-            int x1 = end.X;
-            int y1 = end.Y;
-
-            int dirtyX = Math.Min(x, x1) - thickness;
-            int dirtyY = Math.Min(y, y1) - thickness;
-
-            Int32Rect dirtyRect = new Int32Rect(
-                dirtyX,
-                dirtyY,
-                Math.Max(x1, x) + thickness - dirtyX,
-                Math.Max(y1, y) + thickness - dirtyY);
-            if (AutomaticallyResizeCanvas)
-            {
-                layer.DynamicResizeAbsolute(dirtyRect);
-            }
-
-            x -= layer.OffsetX;
-            y -= layer.OffsetY;
-            x1 -= layer.OffsetX;
-            y1 -= layer.OffsetY;
-
-            paint.StrokeWidth = thickness;
-            paint.Color = color;
-            paint.BlendMode = blendMode;
-            paint.StrokeCap = strokeCap;
-
-            if (thickness == 1)
-            {
-                DrawBresenhamLine(layer, x, y, x1, y1, paint);
-            }
-            else
-            {
-                layer.LayerBitmap.SkiaSurface.Canvas.DrawLine(x, y, x1, y1, paint);
-            }
-
-            layer.InvokeLayerBitmapChange(dirtyRect);
-        }
+            int x1 = start.X;
+            int x2 = end.X;
+            int y1 = start.Y;
+            int y2 = end.Y;
 
 
-        private void DrawBresenhamLine(Layer layer, int x1, int y1, int x2, int y2, SKPaint paint)
-        {
             if (x1 == x2 && y1 == y2)
             if (x1 == x2 && y1 == y2)
             {
             {
-                layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x1, y1, paint);
+                output.Add(start);
                 return;
                 return;
             }
             }
 
 
@@ -227,7 +221,7 @@ namespace PixiEditor.Models.Tools.Tools
                 dy = y1 - y2;
                 dy = y1 - y2;
             }
             }
 
 
-            layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x, y, paint);
+            output.Add(new Coordinates(x, y));
 
 
             if (dx > dy)
             if (dx > dy)
             {
             {
@@ -249,7 +243,7 @@ namespace PixiEditor.Models.Tools.Tools
                         x += xi;
                         x += xi;
                     }
                     }
 
 
-                    layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x, y, paint);
+                    output.Add(new Coordinates(x, y));
                 }
                 }
             }
             }
             else
             else
@@ -272,7 +266,7 @@ namespace PixiEditor.Models.Tools.Tools
                         y += yi;
                         y += yi;
                     }
                     }
 
 
-                    layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(x, y, paint);
+                    output.Add(new Coordinates(x, y));
                 }
                 }
             }
             }
         }
         }

+ 13 - 22
PixiEditor/Models/Tools/Tools/MagicWandTool.cs

@@ -12,11 +12,10 @@ using SkiaSharp;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
 using System.Windows;
 using System.Windows;
-using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class MagicWandTool : ReadonlyTool, ICachedDocumentTool
+    internal class MagicWandTool : ReadonlyTool, ICachedDocumentTool
     {
     {
         private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
         private static Selection ActiveSelection { get => ViewModelMain.Current.BitmapManager.ActiveDocument.ActiveSelection; }
 
 
@@ -29,12 +28,19 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         private Layer cachedDocument;
         private Layer cachedDocument;
 
 
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
+        public MagicWandTool(BitmapManager manager)
         {
         {
-            if (e.LeftButton != MouseButtonState.Pressed)
-            {
+            BitmapManager = manager;
+
+            Toolbar = new MagicWandToolbar();
+
+            ActionDisplay = "Click to flood the selection.";
+        }
+
+        public override void Use(IReadOnlyList<Coordinates> pixels)
+        {
+            if (pixels.Count > 1)
                 return;
                 return;
-            }
 
 
             oldSelection = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
             oldSelection = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
 
 
@@ -61,9 +67,7 @@ namespace PixiEditor.Models.Tools.Tools
 
 
             ToolCalculator.GetLinearFillAbsolute(
             ToolCalculator.GetLinearFillAbsolute(
                    layer,
                    layer,
-                   new Coordinates(
-                       (int)document.MouseXOnCanvas,
-                       (int)document.MouseYOnCanvas),
+                   pixels[0],
                    BitmapManager.ActiveDocument.Width,
                    BitmapManager.ActiveDocument.Width,
                    BitmapManager.ActiveDocument.Height,
                    BitmapManager.ActiveDocument.Height,
                    SKColors.White,
                    SKColors.White,
@@ -74,19 +78,6 @@ namespace PixiEditor.Models.Tools.Tools
             SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelection, selectionType);
             SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelection, selectionType);
         }
         }
 
 
-        public MagicWandTool(BitmapManager manager)
-        {
-            BitmapManager = manager;
-
-            Toolbar = new MagicWandToolbar();
-
-            ActionDisplay = "Click to flood the selection.";
-        }
-
-        public override void Use(List<Coordinates> pixels)
-        {
-        }
-
         public void DocumentChanged()
         public void DocumentChanged()
         {
         {
             cachedDocument = null;
             cachedDocument = null;

+ 53 - 67
PixiEditor/Models/Tools/Tools/MoveTool.cs

@@ -3,24 +3,18 @@ using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.ImageManipulation;
-using PixiEditor.Models.IO;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
-using PixiEditor.ViewModels;
 using SkiaSharp;
 using SkiaSharp;
-using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Windows;
 using System.Windows;
 using System.Windows.Input;
 using System.Windows.Input;
-using Transform = PixiEditor.Models.ImageManipulation.Transform;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class MoveTool : BitmapOperationTool
+    internal class MoveTool : BitmapOperationTool
     {
     {
         private static readonly SKPaint maskingPaint = new()
         private static readonly SKPaint maskingPaint = new()
         {
         {
@@ -38,7 +32,6 @@ namespace PixiEditor.Models.Tools.Tools
         private Surface previewLayerData;
         private Surface previewLayerData;
 
 
         private List<Coordinates> moveStartSelectedPoints = null;
         private List<Coordinates> moveStartSelectedPoints = null;
-        private Coordinates moveStartPos;
         private Int32Rect moveStartRect;
         private Int32Rect moveStartRect;
 
 
         private Coordinates lastDragDelta;
         private Coordinates lastDragDelta;
@@ -63,61 +56,21 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         private BitmapManager BitmapManager { get; }
         private BitmapManager BitmapManager { get; }
 
 
-        public override void OnKeyDown(KeyEventArgs e)
+        public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         {
         {
-            if (e.Key is Key.LeftCtrl or Key.RightCtrl)
-            {
+            if (ctrlIsDown)
                 ActionDisplay = "Hold mouse to move all layers.";
                 ActionDisplay = "Hold mouse to move all layers.";
-            }
-        }
-
-        public override void OnKeyUp(KeyEventArgs e)
-        {
-            if (e.Key is Key.LeftCtrl or Key.RightCtrl)
-            {
+            else
                 ActionDisplay = defaultActionDisplay;
                 ActionDisplay = defaultActionDisplay;
-            }
         }
         }
 
 
-        public override void AddUndoProcess(Document document)
-        {
-            var args = new object[] { change.Document };
-            document.UndoManager.AddUndoChange(change.ToChange(UndoProcess, args));
-            if (moveStartSelectedPoints != null)
-            {
-                SelectionHelpers.AddSelectionUndoStep(document, moveStartSelectedPoints, SelectionType.New);
-                document.UndoManager.SquashUndoChanges(3, "Move selected area");
-                moveStartSelectedPoints = null;
-            }
-            change = null;
-        }
-
-        private void UndoProcess(Layer[] layers, UndoLayer[] data, object[] args)
-        {
-            if (args.Length > 0 && args[0] is Document document)
-            {
-                for (int i = 0; i < layers.Length; i++)
-                {
-                    Layer layer = layers[i];
-                    document.Layers.RemoveAt(data[i].LayerIndex);
-
-                    document.Layers.Insert(data[i].LayerIndex, layer);
-                    if (data[i].IsActive)
-                    {
-                        document.SetMainActiveLayer(data[i].LayerIndex);
-                    }
-                }
-
-            }
-        }
-
-        public override void OnStart(Coordinates startPos)
+        public override void BeforeUse()
         {
         {
             Document doc = BitmapManager.ActiveDocument;
             Document doc = BitmapManager.ActiveDocument;
             Selection selection = doc.ActiveSelection;
             Selection selection = doc.ActiveSelection;
             bool anySelection = selection.SelectedPoints.Any();
             bool anySelection = selection.SelectedPoints.Any();
 
 
-            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+            if (Session.IsCtrlDown)
             {
             {
                 affectedLayers = doc.Layers.Where(x => x.IsVisible).ToArray();
                 affectedLayers = doc.Layers.Where(x => x.IsVisible).ToArray();
             }
             }
@@ -129,10 +82,9 @@ namespace PixiEditor.Models.Tools.Tools
             change = new StorageBasedChange(doc, affectedLayers, true);
             change = new StorageBasedChange(doc, affectedLayers, true);
 
 
             Layer selLayer = selection.SelectionLayer;
             Layer selLayer = selection.SelectionLayer;
-            moveStartRect = anySelection ? 
+            moveStartRect = anySelection ?
                 new(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height) :
                 new(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height) :
-                new (0, 0, doc.Width, doc.Height);
-            moveStartPos = startPos;
+                new(0, 0, doc.Width, doc.Height);
             lastDragDelta = new Coordinates(0, 0);
             lastDragDelta = new Coordinates(0, 0);
 
 
             previewLayerData?.Dispose();
             previewLayerData?.Dispose();
@@ -193,7 +145,7 @@ namespace PixiEditor.Models.Tools.Tools
         {
         {
             using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
             using var selSnap = selLayer.LayerBitmap.SkiaSurface.Snapshot();
             Surface[] output = new Surface[draggedLayers.Length];
             Surface[] output = new Surface[draggedLayers.Length];
-            
+
             int count = 0;
             int count = 0;
             foreach (Layer layer in draggedLayers)
             foreach (Layer layer in draggedLayers)
             {
             {
@@ -209,17 +161,18 @@ namespace PixiEditor.Models.Tools.Tools
                 output[count] = portion;
                 output[count] = portion;
                 count++;
                 count++;
 
 
-                layer.LayerBitmap.SkiaSurface.Canvas.DrawImage(selSnap, new SKRect(0, 0, selLayer.Width, selLayer.Height), 
-                    new SKRect(selLayer.OffsetX - layer.OffsetX, selLayer.OffsetY - layer.OffsetY, selLayer.OffsetX - layer.OffsetX + selLayer.Width, selLayer.OffsetY - layer.OffsetY + selLayer.Height), 
+                layer.LayerBitmap.SkiaSurface.Canvas.DrawImage(selSnap, new SKRect(0, 0, selLayer.Width, selLayer.Height),
+                    new SKRect(selLayer.OffsetX - layer.OffsetX, selLayer.OffsetY - layer.OffsetY, selLayer.OffsetX - layer.OffsetX + selLayer.Width, selLayer.OffsetY - layer.OffsetY + selLayer.Height),
                     inverseMaskingPaint);
                     inverseMaskingPaint);
                 layer.InvokeLayerBitmapChange(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height));
                 layer.InvokeLayerBitmapChange(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height));
             }
             }
             return output;
             return output;
         }
         }
 
 
-        public override void Use(Layer layer, List<Coordinates> mouseMove, SKColor color)
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
-            Coordinates newPos = mouseMove[0];
+            Coordinates newPos = recordedMouseMovement[^1];
+            Coordinates moveStartPos = recordedMouseMovement[0];
             int dX = newPos.X - moveStartPos.X;
             int dX = newPos.X - moveStartPos.X;
             int dY = newPos.Y - moveStartPos.Y;
             int dY = newPos.Y - moveStartPos.Y;
             BitmapManager.ActiveDocument.ActiveSelection.TranslateSelection(dX - lastDragDelta.X, dY - lastDragDelta.Y);
             BitmapManager.ActiveDocument.ActiveSelection.TranslateSelection(dX - lastDragDelta.X, dY - lastDragDelta.Y);
@@ -230,21 +183,22 @@ namespace PixiEditor.Models.Tools.Tools
             int newY = moveStartRect.Y + dY;
             int newY = moveStartRect.Y + dY;
 
 
             Int32Rect dirtyRect = new Int32Rect(newX, newY, moveStartRect.Width, moveStartRect.Height);
             Int32Rect dirtyRect = new Int32Rect(newX, newY, moveStartRect.Width, moveStartRect.Height);
-            layer.DynamicResizeAbsolute(dirtyRect);
-            previewLayerData.SkiaSurface.Draw(layer.LayerBitmap.SkiaSurface.Canvas, newX - layer.OffsetX, newY - layer.OffsetY, Surface.ReplacingPaint);
-            layer.InvokeLayerBitmapChange(dirtyRect);
+            previewLayer.DynamicResizeAbsolute(dirtyRect);
+            previewLayerData.SkiaSurface.Draw(previewLayer.LayerBitmap.SkiaSurface.Canvas, newX - previewLayer.OffsetX, newY - previewLayer.OffsetY, Surface.ReplacingPaint);
+            previewLayer.InvokeLayerBitmapChange(dirtyRect);
         }
         }
 
 
-        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
+        public override void AfterUse()
         {
         {
-            base.OnStoppedRecordingMouseUp(e);
-
+            base.AfterUse();
             BitmapManager.ActiveDocument.PreviewLayer.ClearCanvas();
             BitmapManager.ActiveDocument.PreviewLayer.ClearCanvas();
 
 
             ApplySurfacesToLayers(currentlyDragged, currentlyDraggedPositions, affectedLayers, new Coordinates(lastDragDelta.X, lastDragDelta.Y));
             ApplySurfacesToLayers(currentlyDragged, currentlyDraggedPositions, affectedLayers, new Coordinates(lastDragDelta.X, lastDragDelta.Y));
             foreach (var surface in currentlyDragged)
             foreach (var surface in currentlyDragged)
                 surface.Dispose();
                 surface.Dispose();
             currentlyDragged = null;
             currentlyDragged = null;
+
+            SaveUndo(BitmapManager.ActiveDocument);
         }
         }
 
 
         private static void ApplySurfacesToLayers(Surface[] surfaces, Coordinates[] startPositions, Layer[] layers, Coordinates delta)
         private static void ApplySurfacesToLayers(Surface[] surfaces, Coordinates[] startPositions, Layer[] layers, Coordinates delta)
@@ -263,5 +217,37 @@ namespace PixiEditor.Models.Tools.Tools
                 count++;
                 count++;
             }
             }
         }
         }
+
+        private void SaveUndo(Document document)
+        {
+            var args = new object[] { change.Document };
+            document.UndoManager.AddUndoChange(change.ToChange(UndoProcess, args));
+            if (moveStartSelectedPoints != null)
+            {
+                SelectionHelpers.AddSelectionUndoStep(document, moveStartSelectedPoints, SelectionType.New);
+                document.UndoManager.SquashUndoChanges(3, "Move selected area");
+                moveStartSelectedPoints = null;
+            }
+            change = null;
+        }
+
+        private void UndoProcess(Layer[] layers, UndoLayer[] data, object[] args)
+        {
+            if (args.Length > 0 && args[0] is Document document)
+            {
+                for (int i = 0; i < layers.Length; i++)
+                {
+                    Layer layer = layers[i];
+                    document.Layers.RemoveAt(data[i].LayerIndex);
+
+                    document.Layers.Insert(data[i].LayerIndex, layer);
+                    if (data[i].IsActive)
+                    {
+                        document.SetMainActiveLayer(data[i].LayerIndex);
+                    }
+                }
+
+            }
+        }
     }
     }
 }
 }

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

@@ -1,5 +1,4 @@
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.Position;
+using PixiEditor.Models.Position;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Main;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows.Input;
 using System.Windows.Input;
@@ -20,16 +19,16 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public override bool HideHighlight => true;
         public override bool HideHighlight => true;
         public override string Tooltip => "Move viewport. (H)";
         public override string Tooltip => "Move viewport. (H)";
-
+        /*
         public override void OnMouseUp(MouseEventArgs e)
         public override void OnMouseUp(MouseEventArgs e)
         {
         {
             if (e.MiddleButton == MouseButtonState.Pressed)
             if (e.MiddleButton == MouseButtonState.Pressed)
             {
             {
                 ToolsViewModel.SetActiveTool(ToolsViewModel.LastActionTool);
                 ToolsViewModel.SetActiveTool(ToolsViewModel.LastActionTool);
             }
             }
-        }
+        }*/
 
 
-        public override void Use(List<Coordinates> pixels)
+        public override void Use(IReadOnlyList<Coordinates> pixels)
         {
         {
             // Implemented inside Zoombox.xaml.cs
             // Implemented inside Zoombox.xaml.cs
         }
         }

+ 12 - 13
PixiEditor/Models/Tools/Tools/PenTool.cs

@@ -13,7 +13,7 @@ using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class PenTool : ShapeTool
+    internal class PenTool : ShapeTool
     {
     {
         public Brush Brush { get; set; }
         public Brush Brush { get; set; }
         public List<Brush> Brushes { get; } = new List<Brush>();
         public List<Brush> Brushes { get; } = new List<Brush>();
@@ -50,34 +50,33 @@ namespace PixiEditor.Models.Tools.Tools
         }
         }
 
 
         public override string Tooltip => "Standard brush. (B)";
         public override string Tooltip => "Standard brush. (B)";
-        public override bool UsesShift => false;
 
 
         public bool AutomaticallyResizeCanvas { get; set; } = true;
         public bool AutomaticallyResizeCanvas { get; set; } = true;
 
 
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
+        public override void BeforeUse()
         {
         {
-            base.OnRecordingLeftMouseDown(e);
+            base.BeforeUse();
             changedPixelsindex = 0;
             changedPixelsindex = 0;
             lastChangedPixels = new Coordinates[3];
             lastChangedPixels = new Coordinates[3];
             confirmedPixels.Clear();
             confirmedPixels.Clear();
         }
         }
 
 
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
-            Coordinates startingCords = coordinates.Count > 1 ? coordinates[1] : coordinates[0];
+            Coordinates startingCords = recordedMouseMovement.Count > 1 ? recordedMouseMovement[^2] : recordedMouseMovement[0];
             paint.Color = color;
             paint.Color = color;
             if (AutomaticallyResizeCanvas)
             if (AutomaticallyResizeCanvas)
             {
             {
-                int maxX = coordinates.Max(x => x.X);
-                int maxY = coordinates.Max(x => x.Y);
-                int minX = coordinates.Min(x => x.X);
-                int minY = coordinates.Min(x => x.Y);
-                layer.DynamicResizeAbsolute(new(minX, minY, maxX - minX + 1, maxX - minX + 1));
+                int maxX = recordedMouseMovement.Max(x => x.X);
+                int maxY = recordedMouseMovement.Max(x => x.Y);
+                int minX = recordedMouseMovement.Min(x => x.X);
+                int minY = recordedMouseMovement.Min(x => x.Y);
+                previewLayer.DynamicResizeAbsolute(new(minX, minY, maxX - minX + 1, maxX - minX + 1));
             }
             }
             Draw(
             Draw(
-                layer,
+                previewLayer,
                 startingCords,
                 startingCords,
-                coordinates[0],
+                recordedMouseMovement[^1],
                 color,
                 color,
                 toolSizeSetting.Value,
                 toolSizeSetting.Value,
                 pixelPerfectSetting.Value,
                 pixelPerfectSetting.Value,

+ 11 - 22
PixiEditor/Models/Tools/Tools/RectangleTool.cs

@@ -1,11 +1,11 @@
-using PixiEditor.Models.Layers;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using SkiaSharp;
 using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Windows;
 using System.Windows;
-using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
@@ -21,23 +21,15 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public bool Filled { get; set; } = false;
         public bool Filled { get; set; } = false;
 
 
-        public override void OnKeyDown(KeyEventArgs e)
+        public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
         {
         {
-            if (e.Key is Key.LeftShift or Key.RightShift)
-            {
+            if (shiftIsDown)
                 ActionDisplay = "Click and move to draw a square.";
                 ActionDisplay = "Click and move to draw a square.";
-            }
-        }
-
-        public override void OnKeyUp(KeyEventArgs e)
-        {
-            if (e.Key is Key.LeftShift or Key.RightShift)
-            {
+            else
                 ActionDisplay = defaultActionDisplay;
                 ActionDisplay = defaultActionDisplay;
-            }
         }
         }
 
 
-        public override void Use(Layer layer, List<Coordinates> coordinates, SKColor color)
+        public override void Use(Layer activeLayer, Layer previewLayer, IEnumerable<Layer> allLayers, IReadOnlyList<Coordinates> recordedMouseMovement, SKColor color)
         {
         {
             int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
             int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
             SKColor? fillColor = null;
             SKColor? fillColor = null;
@@ -46,12 +38,14 @@ namespace PixiEditor.Models.Tools.Tools
                 var temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 var temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
                 fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
                 fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);
             }
             }
-            CreateRectangle(layer, color, fillColor, coordinates, thickness);
+            CreateRectangle(previewLayer, color, fillColor, recordedMouseMovement, thickness);
         }
         }
 
 
-        public void CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, List<Coordinates> coordinates, int thickness)
+        private void CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, IReadOnlyList<Coordinates> coordinates, int thickness)
         {
         {
-            DoubleCoords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
+            var (start, end) = Session.IsShiftDown ? CoordinatesHelper.GetSquareCoordiantes(coordinates) : (coordinates[0], coordinates[^1]);
+
+            DoubleCoords fixedCoordinates = CalculateCoordinatesForShapeRotation(start, end);
 
 
             int halfThickness = (int)Math.Ceiling(thickness / 2.0);
             int halfThickness = (int)Math.Ceiling(thickness / 2.0);
             Int32Rect dirtyRect = new Int32Rect(
             Int32Rect dirtyRect = new Int32Rect(
@@ -83,10 +77,5 @@ namespace PixiEditor.Models.Tools.Tools
             }
             }
             layer.InvokeLayerBitmapChange(dirtyRect);
             layer.InvokeLayerBitmapChange(dirtyRect);
         }
         }
-
-        public void CreateRectangle(Layer layer, SKColor color, SKColor? fillColor, Coordinates start, Coordinates end, int thickness)
-        {
-            CreateRectangle(layer, color, fillColor, new() { end, start }, thickness);
-        }
     }
     }
 }
 }

+ 12 - 16
PixiEditor/Models/Tools/Tools/SelectTool.cs

@@ -1,25 +1,19 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Linq;
-using System.Windows.Controls;
-using System.Windows.Input;
-using PixiEditor.Helpers;
+using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Position;
-using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
 using PixiEditor.Models.Tools.ToolSettings.Toolbars;
-using PixiEditor.Models.Undo;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class SelectTool : ReadonlyTool
+    internal class SelectTool : ReadonlyTool
     {
     {
         private readonly RectangleTool rectangleTool;
         private readonly RectangleTool rectangleTool;
         private readonly CircleTool circleTool;
         private readonly CircleTool circleTool;
@@ -43,15 +37,17 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public override string Tooltip => "Selects area. (M)";
         public override string Tooltip => "Selects area. (M)";
 
 
-        public override void OnRecordingLeftMouseDown(MouseEventArgs e)
+        public override void BeforeUse()
         {
         {
+            base.BeforeUse();
             SelectionType = Toolbar.GetEnumSetting<SelectionType>("SelectMode").Value;
             SelectionType = Toolbar.GetEnumSetting<SelectionType>("SelectMode").Value;
 
 
             oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
             oldSelectedPoints = new ReadOnlyCollection<Coordinates>(ActiveSelection.SelectedPoints);
         }
         }
 
 
-        public override void OnStoppedRecordingMouseUp(MouseEventArgs e)
+        public override void AfterUse()
         {
         {
+            base.AfterUse();
             if (ActiveSelection.SelectedPoints.Count <= 1)
             if (ActiveSelection.SelectedPoints.Count <= 1)
             {
             {
                 // If we have not selected multiple points, clear the selection
                 // If we have not selected multiple points, clear the selection
@@ -61,7 +57,7 @@ namespace PixiEditor.Models.Tools.Tools
             SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelectedPoints, SelectionType);
             SelectionHelpers.AddSelectionUndoStep(ViewModelMain.Current.BitmapManager.ActiveDocument, oldSelectedPoints, SelectionType);
         }
         }
 
 
-        public override void Use(List<Coordinates> pixels)
+        public override void Use(IReadOnlyList<Coordinates> pixels)
         {
         {
             Select(pixels, Toolbar.GetEnumSetting<SelectionShape>("SelectShape").Value);
             Select(pixels, Toolbar.GetEnumSetting<SelectionShape>("SelectShape").Value);
         }
         }
@@ -100,7 +96,7 @@ namespace PixiEditor.Models.Tools.Tools
             return GetRectangleSelectionForPoints(new Coordinates(0, 0), new Coordinates(document.Width - 1, document.Height - 1));
             return GetRectangleSelectionForPoints(new Coordinates(0, 0), new Coordinates(document.Width - 1, document.Height - 1));
         }
         }
 
 
-        private void Select(List<Coordinates> pixels, SelectionShape shape)
+        private void Select(IReadOnlyList<Coordinates> pixels, SelectionShape shape)
         {
         {
             IEnumerable<Coordinates> selection;
             IEnumerable<Coordinates> selection;
 
 
@@ -126,4 +122,4 @@ namespace PixiEditor.Models.Tools.Tools
             BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selection, SelectionType);
             BitmapManager.ActiveDocument.ActiveSelection.SetSelection(selection, SelectionType);
         }
         }
     }
     }
-}
+}

+ 6 - 7
PixiEditor/Models/Tools/Tools/ZoomTool.cs

@@ -5,14 +5,13 @@ using System.Windows.Input;
 
 
 namespace PixiEditor.Models.Tools.Tools
 namespace PixiEditor.Models.Tools.Tools
 {
 {
-    public class ZoomTool : ReadonlyTool
+    internal class ZoomTool : ReadonlyTool
     {
     {
         private BitmapManager BitmapManager { get; }
         private BitmapManager BitmapManager { get; }
         private string defaultActionDisplay = "Click and move to zoom. Click to zoom in, hold ctrl and click to zoom out.";
         private string defaultActionDisplay = "Click and move to zoom. Click to zoom in, hold ctrl and click to zoom out.";
 
 
         public ZoomTool(BitmapManager bitmapManager)
         public ZoomTool(BitmapManager bitmapManager)
         {
         {
-            CanStartOutsideCanvas = true;
             ActionDisplay = defaultActionDisplay;
             ActionDisplay = defaultActionDisplay;
             BitmapManager = bitmapManager;
             BitmapManager = bitmapManager;
         }
         }
@@ -21,23 +20,23 @@ namespace PixiEditor.Models.Tools.Tools
 
 
         public override string Tooltip => "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
         public override string Tooltip => "Zooms viewport (Z). Click to zoom in, hold alt and click to zoom out.";
 
 
-        public override void OnKeyDown(KeyEventArgs e)
+        public override void OnKeyDown(Key key)
         {
         {
-            if (e.Key is Key.LeftCtrl or Key.RightCtrl)
+            if (key is Key.LeftCtrl)
             {
             {
                 ActionDisplay = "Click and move to zoom. Click to zoom out, release ctrl and click to zoom in.";
                 ActionDisplay = "Click and move to zoom. Click to zoom out, release ctrl and click to zoom in.";
             }
             }
         }
         }
 
 
-        public override void OnKeyUp(KeyEventArgs e)
+        public override void OnKeyUp(Key key)
         {
         {
-            if (e.Key is Key.LeftCtrl or Key.RightCtrl)
+            if (key is Key.LeftCtrl)
             {
             {
                 ActionDisplay = defaultActionDisplay;
                 ActionDisplay = defaultActionDisplay;
             }
             }
         }
         }
 
 
-        public override void Use(List<Coordinates> pixels)
+        public override void Use(IReadOnlyList<Coordinates> pixels)
         {
         {
             // Implemented inside Zoombox.xaml.cs
             // Implemented inside Zoombox.xaml.cs
         }
         }

+ 45 - 79
PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -1,9 +1,7 @@
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers.Shortcuts;
 using PixiEditor.Models.Controllers.Shortcuts;
-using PixiEditor.Models.Position;
 using System;
 using System;
-using System.Windows;
 using System.Windows.Input;
 using System.Windows.Input;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
@@ -14,35 +12,32 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         public RelayCommand MouseDownCommand { get; set; }
         public RelayCommand MouseDownCommand { get; set; }
 
 
+        public RelayCommand MouseUpCommand { get; set; }
+
         public RelayCommand KeyDownCommand { get; set; }
         public RelayCommand KeyDownCommand { get; set; }
 
 
         public RelayCommand KeyUpCommand { get; set; }
         public RelayCommand KeyUpCommand { get; set; }
 
 
         private bool restoreToolOnKeyUp = false;
         private bool restoreToolOnKeyUp = false;
 
 
+        private MouseInputFilter filter = new();
+
         public IoViewModel(ViewModelMain owner)
         public IoViewModel(ViewModelMain owner)
             : base(owner)
             : base(owner)
         {
         {
-            MouseMoveCommand = new RelayCommand(MouseMove);
-            MouseDownCommand = new RelayCommand(MouseDown);
-            KeyDownCommand = new RelayCommand(KeyDown);
-            KeyUpCommand = new RelayCommand(KeyUp);
-        }
-
-        public void MouseHook_OnMouseUp(object sender, Point p, MouseButton button)
-        {
-            GlobalMouseHook.OnMouseUp -= MouseHook_OnMouseUp;
-            if (button == MouseButton.Left)
-            {
-                Owner.BitmapManager.MouseController.StopRecordingMouseMovementChanges();
-            }
-
-            Owner.BitmapManager.MouseController.MouseUp(new MouseEventArgs(
-                Mouse.PrimaryDevice,
-                (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+            MouseDownCommand = new RelayCommand(filter.MouseDown);
+            MouseMoveCommand = new RelayCommand(filter.MouseMove);
+            MouseUpCommand = new RelayCommand(filter.MouseUp);
+            GlobalMouseHook.OnMouseUp += filter.MouseUp;
+            KeyDownCommand = new RelayCommand(OnKeyDown);
+            KeyUpCommand = new RelayCommand(OnKeyUp);
+
+            filter.OnMouseDown += OnMouseDown;
+            filter.OnMouseMove += OnMouseMove;
+            filter.OnMouseUp += OnMouseUp;
         }
         }
 
 
-        public void KeyDown(object parameter)
+        private void OnKeyDown(object parameter)
         {
         {
             KeyEventArgs args = (KeyEventArgs)parameter;
             KeyEventArgs args = (KeyEventArgs)parameter;
             if (args.IsRepeat && !restoreToolOnKeyUp && Owner.ShortcutController.LastShortcut != null &&
             if (args.IsRepeat && !restoreToolOnKeyUp && Owner.ShortcutController.LastShortcut != null &&
@@ -53,82 +48,53 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
 
 
             Owner.ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
             Owner.ShortcutController.KeyPressed(args.Key, Keyboard.Modifiers);
-            Owner.ToolsSubViewModel.ActiveTool.OnKeyDown(args);
+
+            if (Owner.BitmapManager.ActiveDocument != null)
+                Owner.BitmapManager.InputTarget.OnKeyDown(args.Key);
         }
         }
 
 
-        private void MouseDown(object parameter)
+        private void OnKeyUp(object parameter)
         {
         {
-            if (Owner.BitmapManager.ActiveDocument == null || Owner.BitmapManager.ActiveDocument.Layers.Count == 0)
+            KeyEventArgs args = (KeyEventArgs)parameter;
+            if (restoreToolOnKeyUp && Owner.ShortcutController.LastShortcut != null &&
+                Owner.ShortcutController.LastShortcut.ShortcutKey == args.Key)
             {
             {
-                return;
+                restoreToolOnKeyUp = false;
+                Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
+                ShortcutController.BlockShortcutExecution = false;
             }
             }
 
 
-            if (Mouse.LeftButton == MouseButtonState.Pressed)
+            if (Owner.BitmapManager.ActiveDocument != null)
+                Owner.BitmapManager.InputTarget.OnKeyUp(args.Key);
+        }
+
+        private void OnMouseDown(object sender, MouseButton button)
+        {
+            if (button == MouseButton.Left)
             {
             {
                 BitmapManager bitmapManager = Owner.BitmapManager;
                 BitmapManager bitmapManager = Owner.BitmapManager;
                 var activeDocument = bitmapManager.ActiveDocument;
                 var activeDocument = bitmapManager.ActiveDocument;
-                if (!bitmapManager.MouseController.IsRecordingChanges)
-                {
-                    bool clickedOnCanvas = activeDocument.MouseXOnCanvas >= 0 &&
-                        activeDocument.MouseXOnCanvas <= activeDocument.Width &&
-                        activeDocument.MouseYOnCanvas >= 0 &&
-                        activeDocument.MouseYOnCanvas <= activeDocument.Height;
-                    bitmapManager.MouseController.StartRecordingMouseMovementChanges(clickedOnCanvas);
-                    bitmapManager.MouseController.RecordMouseMovementChange(MousePositionConverter.CurrentCoordinates);
-                }
-            }
-
-            Owner.BitmapManager.MouseController.MouseDown(new MouseEventArgs(
-                Mouse.PrimaryDevice,
-                (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()));
+                if (activeDocument == null)
+                    return;
 
 
-            Coordinates cords = new Coordinates(
-                (int)Owner.BitmapManager.ActiveDocument.MouseXOnCanvas,
-                (int)Owner.BitmapManager.ActiveDocument.MouseYOnCanvas);
-            Owner.BitmapManager.MouseController.MouseDownCoordinates(cords);
-
-            // Mouse down is guaranteed to only be raised from within this application, so by subscribing here we
-            // only listen for mouse up events that occurred as a result of a mouse down within this application.
-            // This seems better than maintaining a global listener indefinitely.
-            GlobalMouseHook.OnMouseUp += MouseHook_OnMouseUp;
+                bitmapManager.InputTarget.OnLeftMouseButtonDown(activeDocument.MouseXOnCanvas, activeDocument.MouseYOnCanvas);
+            }
         }
         }
 
 
-        /// <summary>
-        ///     Method connected with command, it executes tool "activity".
-        /// </summary>
-        /// <param name="parameter">CommandParameter.</param>
-        private void MouseMove(object parameter)
+        private void OnMouseMove(object sender, EventArgs args)
         {
         {
-            if (Owner.BitmapManager.ActiveDocument == null)
-            {
+            var activeDocument = Owner.BitmapManager.ActiveDocument;
+            if (activeDocument == null)
                 return;
                 return;
-            }
-
-            Coordinates cords = new Coordinates(
-                (int)Owner.BitmapManager.ActiveDocument.MouseXOnCanvas,
-                (int)Owner.BitmapManager.ActiveDocument.MouseYOnCanvas);
-            MousePositionConverter.CurrentCoordinates = cords;
-
-            if (Owner.BitmapManager.MouseController.IsRecordingChanges && Mouse.LeftButton == MouseButtonState.Pressed)
-            {
-                Owner.BitmapManager.MouseController.RecordMouseMovementChange(cords);
-            }
-
-            Owner.BitmapManager.MouseController.MouseMoved(cords);
+            Owner.BitmapManager.InputTarget.OnMouseMove(activeDocument.MouseXOnCanvas, activeDocument.MouseYOnCanvas);
         }
         }
 
 
-        private void KeyUp(object parameter)
+        private void OnMouseUp(object sender, MouseButton button)
         {
         {
-            KeyEventArgs args = (KeyEventArgs)parameter;
-            if (restoreToolOnKeyUp && Owner.ShortcutController.LastShortcut != null &&
-                Owner.ShortcutController.LastShortcut.ShortcutKey == args.Key)
-            {
-                restoreToolOnKeyUp = false;
-                Owner.ToolsSubViewModel.SetActiveTool(Owner.ToolsSubViewModel.LastActionTool);
-                ShortcutController.BlockShortcutExecution = false;
-            }
-
-            Owner.ToolsSubViewModel.ActiveTool.OnKeyUp(args);
+            if (Owner.BitmapManager.ActiveDocument == null)
+                return;
+            if (button == MouseButton.Left)
+                Owner.BitmapManager.InputTarget.OnLeftMouseButtonUp();
         }
         }
     }
     }
 }
 }

+ 4 - 6
PixiEditor/ViewModels/SubViewModels/Main/StylusViewModel.cs

@@ -1,10 +1,8 @@
-using System.Windows;
-using System.Windows.Input;
-using GalaSoft.MvvmLight.CommandWpf;
-using PixiEditor.Models.Position;
+using GalaSoft.MvvmLight.CommandWpf;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.Models.UserPreferences;
+using System.Windows.Input;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
@@ -75,7 +73,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
 
 
         private void StylusOutOfRange(StylusEventArgs e)
         private void StylusOutOfRange(StylusEventArgs e)
         {
         {
-            Owner.BitmapManager.HighlightPixels(new Coordinates(-1, -1));
+            Owner.BitmapManager.UpdateHighlightIfNecessary(true);
         }
         }
 
 
         private void StylusSystemGesture(StylusSystemGestureEventArgs e)
         private void StylusSystemGesture(StylusSystemGestureEventArgs e)
@@ -110,4 +108,4 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
         }
         }
     }
     }
-}
+}

+ 11 - 15
PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -1,19 +1,14 @@
-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 Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
-using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Events;
-using PixiEditor.Models.Position;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.Tools;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
 using PixiEditor.Models.Tools.ToolSettings.Settings;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Input;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
@@ -41,7 +36,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
         public Tool ActiveTool
         public Tool ActiveTool
         {
         {
             get => activeTool;
             get => activeTool;
-            set => SetProperty(ref activeTool, value);
+            private set => SetProperty(ref activeTool, value);
         }
         }
 
 
         public int ToolSize
         public int ToolSize
@@ -54,7 +49,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
                 if (ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is SizeSetting toolSize)
                 if (ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is SizeSetting toolSize)
                 {
                 {
                     toolSize.Value = value;
                     toolSize.Value = value;
-                    Owner.BitmapManager.HighlightPixels(MousePositionConverter.CurrentCoordinates);
+                    Owner.BitmapManager.UpdateHighlightIfNecessary();
                 }
                 }
             }
             }
         }
         }
@@ -90,15 +85,16 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             if (ActiveTool != null)
             if (ActiveTool != null)
             {
             {
                 activeTool.IsActive = false;
                 activeTool.IsActive = false;
-                ActiveTool.OnDeselected();
             }
             }
 
 
             LastActionTool = ActiveTool;
             LastActionTool = ActiveTool;
             ActiveTool = tool;
             ActiveTool = tool;
             SelectedToolChanged?.Invoke(this, new SelectedToolEventArgs(LastActionTool, ActiveTool));
             SelectedToolChanged?.Invoke(this, new SelectedToolEventArgs(LastActionTool, ActiveTool));
 
 
+            //update new tool
+            Owner.BitmapManager.UpdateActionDisplay();
+
             tool.IsActive = true;
             tool.IsActive = true;
-            ActiveTool.OnSelected();
             SetToolCursor(tool.GetType());
             SetToolCursor(tool.GetType());
 
 
             if (Owner.StylusSubViewModel != null)
             if (Owner.StylusSubViewModel != null)
@@ -188,4 +184,4 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             }
             }
         }
         }
     }
     }
-}
+}

+ 3 - 16
PixiEditor/ViewModels/SubViewModels/Main/UndoViewModel.cs

@@ -1,11 +1,6 @@
-using System;
-using System.IO;
-using System.Linq;
-using PixiEditor.Helpers;
-using PixiEditor.Models.Controllers;
-using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.Tools;
+using PixiEditor.Helpers;
 using PixiEditor.Models.Undo;
 using PixiEditor.Models.Undo;
+using System.IO;
 
 
 namespace PixiEditor.ViewModels.SubViewModels.Main
 namespace PixiEditor.ViewModels.SubViewModels.Main
 {
 {
@@ -28,14 +23,6 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             ClearUndoTempDirectory();
             ClearUndoTempDirectory();
         }
         }
 
 
-        public void TriggerNewUndoChange(Tool selectedTool)
-        {
-            var activeDoc = Owner.BitmapManager.ActiveDocument;
-            if (activeDoc is null) return;
-
-            selectedTool.AddUndoProcess(activeDoc);
-        }
-
         /// <summary>
         /// <summary>
         ///     Redo last action.
         ///     Redo last action.
         /// </summary>
         /// </summary>
@@ -86,4 +73,4 @@ namespace PixiEditor.ViewModels.SubViewModels.Main
             return Owner.BitmapManager.ActiveDocument?.UndoManager.CanRedo ?? false;
             return Owner.BitmapManager.ActiveDocument?.UndoManager.CanRedo ?? false;
         }
         }
     }
     }
-}
+}

+ 4 - 9
PixiEditor/ViewModels/ViewModelMain.cs

@@ -132,7 +132,6 @@ namespace PixiEditor.ViewModels
             Preferences.Init();
             Preferences.Init();
             BitmapManager = services.GetRequiredService<BitmapManager>();
             BitmapManager = services.GetRequiredService<BitmapManager>();
             BitmapManager.BitmapOperations.BitmapChanged += BitmapUtility_BitmapChanged;
             BitmapManager.BitmapOperations.BitmapChanged += BitmapUtility_BitmapChanged;
-            BitmapManager.MouseController.StoppedRecordingChanges += MouseController_StoppedRecordingChanges;
             BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
             BitmapManager.DocumentChanged += BitmapManager_DocumentChanged;
 
 
             SelectionSubViewModel = services.GetService<SelectionViewModel>();
             SelectionSubViewModel = services.GetService<SelectionViewModel>();
@@ -142,6 +141,7 @@ namespace PixiEditor.ViewModels
 
 
             FileSubViewModel = services.GetService<FileViewModel>();
             FileSubViewModel = services.GetService<FileViewModel>();
             ToolsSubViewModel = services.GetService<ToolsViewModel>();
             ToolsSubViewModel = services.GetService<ToolsViewModel>();
+            ToolsSubViewModel.SelectedToolChanged += BitmapManager_SelectedToolChanged;
             ToolsSubViewModel?.SetupTools(services);
             ToolsSubViewModel?.SetupTools(services);
 
 
             IoSubViewModel = services.GetService<IoViewModel>();
             IoSubViewModel = services.GetService<IoViewModel>();
@@ -218,8 +218,6 @@ namespace PixiEditor.ViewModels
                         new Shortcut(Key.F1, MiscSubViewModel.OpenShortcutWindowCommand, "Open the shortcut window", true)));
                         new Shortcut(Key.F1, MiscSubViewModel.OpenShortcutWindowCommand, "Open the shortcut window", true)));
 
 
             BitmapManager.PrimaryColor = ColorsSubViewModel.PrimaryColor;
             BitmapManager.PrimaryColor = ColorsSubViewModel.PrimaryColor;
-
-            ToolsSubViewModel.SelectedToolChanged += BitmapManager_SelectedToolChanged;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -249,10 +247,12 @@ namespace PixiEditor.ViewModels
 
 
         private void BitmapManager_SelectedToolChanged(object sender, SelectedToolEventArgs e)
         private void BitmapManager_SelectedToolChanged(object sender, SelectedToolEventArgs e)
         {
         {
-            e.OldTool.PropertyChanged -= SelectedTool_PropertyChanged;
+            if (e.OldTool != null)
+                e.OldTool.PropertyChanged -= SelectedTool_PropertyChanged;
             e.NewTool.PropertyChanged += SelectedTool_PropertyChanged;
             e.NewTool.PropertyChanged += SelectedTool_PropertyChanged;
 
 
             NotifyToolActionDisplayChanged();
             NotifyToolActionDisplayChanged();
+            BitmapManager.InputTarget.OnToolChange(e.NewTool);
         }
         }
 
 
         private void SelectedTool_PropertyChanged(object sender, PropertyChangedEventArgs e)
         private void SelectedTool_PropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -360,11 +360,6 @@ namespace PixiEditor.ViewModels
             BitmapManager.ActiveDocument.CenterViewportTrigger.Execute(this, new Size(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
             BitmapManager.ActiveDocument.CenterViewportTrigger.Execute(this, new Size(BitmapManager.ActiveDocument.Width, BitmapManager.ActiveDocument.Height));
         }
         }
 
 
-        private void MouseController_StoppedRecordingChanges(object sender, EventArgs e)
-        {
-            UndoSubViewModel.TriggerNewUndoChange(ToolsSubViewModel.ActiveTool);
-        }
-
         private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
         private void BitmapUtility_BitmapChanged(object sender, BitmapChangedEventArgs e)
         {
         {
             BitmapManager.ActiveDocument.ChangesSaved = false;
             BitmapManager.ActiveDocument.ChangesSaved = false;

+ 1 - 0
PixiEditor/Views/MainWindow.xaml

@@ -285,6 +285,7 @@
                                         MiddleMouseClickedCommand="{Binding XamlAccesibleViewModel.ToolsSubViewModel.SelectToolCommand}"
                                         MiddleMouseClickedCommand="{Binding XamlAccesibleViewModel.ToolsSubViewModel.SelectToolCommand}"
                                         MouseMoveCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseMoveCommand}"
                                         MouseMoveCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseMoveCommand}"
                                         MouseDownCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseDownCommand}"
                                         MouseDownCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseDownCommand}"
+                                        MouseUpCommand="{Binding XamlAccesibleViewModel.IoSubViewModel.MouseUpCommand}"
                                         MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
                                         MouseXOnCanvas="{Binding MouseXOnCanvas, Mode=TwoWay}"
                                         MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}"
                                         MouseYOnCanvas="{Binding MouseYOnCanvas, Mode=TwoWay}"
                                         StylusButtonDownCommand="{Binding XamlAccesibleViewModel.StylusSubViewModel.StylusDownCommand}"
                                         StylusButtonDownCommand="{Binding XamlAccesibleViewModel.StylusSubViewModel.StylusDownCommand}"

+ 5 - 2
PixiEditor/Views/UserControls/DrawingViewPort.xaml

@@ -24,10 +24,13 @@
                    UseTouchGestures="{Binding UseTouchGestures, ElementName=uc}">
                    UseTouchGestures="{Binding UseTouchGestures, ElementName=uc}">
         <i:Interaction.Triggers>
         <i:Interaction.Triggers>
             <i:EventTrigger EventName="MouseMove">
             <i:EventTrigger EventName="MouseMove">
-                <i:InvokeCommandAction Command="{Binding MouseMoveCommand, ElementName=uc}" />
+                <cmd:EventToCommand Command="{Binding MouseMoveCommand, ElementName=uc}" PassEventArgsToCommand="True" />
             </i:EventTrigger>
             </i:EventTrigger>
             <i:EventTrigger EventName="MouseDown">
             <i:EventTrigger EventName="MouseDown">
-                <i:InvokeCommandAction Command="{Binding MouseDownCommand, ElementName=uc}"/>
+                <cmd:EventToCommand Command="{Binding MouseDownCommand, ElementName=uc}" PassEventArgsToCommand="True" />
+            </i:EventTrigger>
+            <i:EventTrigger EventName="MouseUp">
+                <cmd:EventToCommand Command="{Binding MouseUpCommand, ElementName=uc}" PassEventArgsToCommand="True" />
             </i:EventTrigger>
             </i:EventTrigger>
             <i:EventTrigger EventName="PreviewMouseDown">
             <i:EventTrigger EventName="PreviewMouseDown">
                 <i:InvokeCommandAction Command="{Binding PreviewMouseDownCommand, ElementName=uc}"/>
                 <i:InvokeCommandAction Command="{Binding PreviewMouseDownCommand, ElementName=uc}"/>

+ 10 - 1
PixiEditor/Views/UserControls/DrawingViewPort.xaml.cs

@@ -20,7 +20,10 @@ namespace PixiEditor.Views.UserControls
 
 
         public static readonly DependencyProperty MouseDownCommandProperty =
         public static readonly DependencyProperty MouseDownCommandProperty =
             DependencyProperty.Register(nameof(MouseDownCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
             DependencyProperty.Register(nameof(MouseDownCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
-        
+
+        public static readonly DependencyProperty MouseUpCommandProperty =
+            DependencyProperty.Register(nameof(MouseUpCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
+
         public static readonly DependencyProperty StylusButtonDownCommandProperty =
         public static readonly DependencyProperty StylusButtonDownCommandProperty =
             DependencyProperty.Register(nameof(StylusButtonDownCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
             DependencyProperty.Register(nameof(StylusButtonDownCommand), typeof(ICommand), typeof(DrawingViewPort), new PropertyMetadata(default(ICommand)));
 
 
@@ -77,6 +80,12 @@ namespace PixiEditor.Views.UserControls
             set => SetValue(MouseDownCommandProperty, value);
             set => SetValue(MouseDownCommandProperty, value);
         }
         }
 
 
+        public ICommand MouseUpCommand
+        {
+            get => (ICommand)GetValue(MouseUpCommandProperty);
+            set => SetValue(MouseUpCommandProperty, value);
+        }
+
         public ICommand StylusButtonDownCommand
         public ICommand StylusButtonDownCommand
         {
         {
             get => (ICommand)GetValue(StylusButtonDownCommandProperty);
             get => (ICommand)GetValue(StylusButtonDownCommandProperty);

+ 2 - 2
PixiEditor/Views/UserControls/Layers/LayerGroupControl.xaml.cs

@@ -61,12 +61,12 @@ namespace PixiEditor.Views.UserControls.Layers
             LayerGroupControl control = (LayerGroupControl)d;
             LayerGroupControl control = (LayerGroupControl)d;
             if (e.OldValue is LayersViewModel oldVm && oldVm != e.NewValue)
             if (e.OldValue is LayersViewModel oldVm && oldVm != e.NewValue)
             {
             {
-                oldVm.Owner.BitmapManager.MouseController.StoppedRecordingChanges -= control.MouseController_StoppedRecordingChanges;
+                oldVm.Owner.BitmapManager.StopUsingTool -= control.MouseController_StoppedRecordingChanges;
             }
             }
 
 
             if (e.NewValue is LayersViewModel vm)
             if (e.NewValue is LayersViewModel vm)
             {
             {
-                vm.Owner.BitmapManager.MouseController.StoppedRecordingChanges += control.MouseController_StoppedRecordingChanges;
+                vm.Owner.BitmapManager.StopUsingTool += control.MouseController_StoppedRecordingChanges;
             }
             }
         }
         }
 
 

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

@@ -10,7 +10,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         public void TestThatStartRecordingMouseMovChangesStartsRecordingAndInvokesEvent()
         public void TestThatStartRecordingMouseMovChangesStartsRecordingAndInvokesEvent()
         {
         {
             bool eventInvoked = false;
             bool eventInvoked = false;
-            MouseMovementController controller = new MouseMovementController();
+            ToolSessionController controller = new ToolSessionController();
             controller.StartedRecordingChanges += (sender, e) => eventInvoked = true;
             controller.StartedRecordingChanges += (sender, e) => eventInvoked = true;
 
 
             controller.StartRecordingMouseMovementChanges(false);
             controller.StartRecordingMouseMovementChanges(false);
@@ -23,7 +23,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         [Fact]
         [Fact]
         public void TestThatRecordMouseMovementChangeRecordsMouseMovementChange()
         public void TestThatRecordMouseMovementChangeRecordsMouseMovementChange()
         {
         {
-            MouseMovementController controller = new MouseMovementController();
+            ToolSessionController controller = new ToolSessionController();
             controller.StartRecordingMouseMovementChanges(false);
             controller.StartRecordingMouseMovementChanges(false);
             controller.RecordMouseMovementChange(new Coordinates(5, 5));
             controller.RecordMouseMovementChange(new Coordinates(5, 5));
 
 
@@ -39,14 +39,14 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
             Coordinates position = new Coordinates(5, 5);
             Coordinates position = new Coordinates(5, 5);
             MouseMovementEventArgs args = new MouseMovementEventArgs(default(Coordinates));
             MouseMovementEventArgs args = new MouseMovementEventArgs(default(Coordinates));
 
 
-            MouseMovementController controller = new MouseMovementController();
-            controller.MousePositionChanged += (s, e) =>
+            ToolSessionController controller = new ToolSessionController();
+            controller.PreciseMousePositionChanged += (s, e) =>
             {
             {
                 eventRaised = true;
                 eventRaised = true;
                 args = e;
                 args = e;
             };
             };
 
 
-            controller.MouseMoved(position);
+            controller.OnMouseMove(position);
 
 
             Assert.True(eventRaised);
             Assert.True(eventRaised);
             Assert.Equal(position, args.NewPosition);
             Assert.Equal(position, args.NewPosition);
@@ -55,7 +55,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         [Fact]
         [Fact]
         public void TestStopRecordingChangesStopsRecording()
         public void TestStopRecordingChangesStopsRecording()
         {
         {
-            MouseMovementController controller = new MouseMovementController();
+            ToolSessionController controller = new ToolSessionController();
 
 
             controller.StartRecordingMouseMovementChanges(true);
             controller.StartRecordingMouseMovementChanges(true);
             controller.StopRecordingMouseMovementChanges();
             controller.StopRecordingMouseMovementChanges();
@@ -67,7 +67,7 @@ namespace PixiEditorTests.ModelsTests.ControllersTests
         [Fact]
         [Fact]
         public void TestThatRecordChangesNotRecords()
         public void TestThatRecordChangesNotRecords()
         {
         {
-            MouseMovementController controller = new MouseMovementController();
+            ToolSessionController controller = new ToolSessionController();
             controller.RecordMouseMovementChange(new Coordinates(5, 10));
             controller.RecordMouseMovementChange(new Coordinates(5, 10));
 
 
             Assert.False(controller.IsRecordingChanges);
             Assert.False(controller.IsRecordingChanges);