Selaa lähdekoodia

Pen tool works

Equbuxu 3 vuotta sitten
vanhempi
commit
1daf7f7152
25 muutettua tiedostoa jossa 273 lisäystä ja 213 poistoa
  1. 4 4
      src/.editorconfig
  2. 10 0
      src/PixiEditor/GlobalUsings.cs
  3. 1 1
      src/PixiEditor/Helpers/Behaviours/GlobalShortcutFocusBehavior.cs
  4. 1 1
      src/PixiEditor/Models/Commands/CommandCollection.cs
  5. 1 1
      src/PixiEditor/Models/Controllers/ShortcutController.cs
  6. 55 67
      src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs
  7. 2 0
      src/PixiEditor/Models/DocumentModels/DocumentHelpers.cs
  8. 63 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineBasedPenExecutor.cs
  9. 36 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs
  10. 19 72
      src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs
  11. 0 5
      src/PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs
  12. 4 4
      src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  13. 11 11
      src/PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs
  14. 18 20
      src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs
  15. 2 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tool.cs
  16. 2 2
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Settings/SizeSetting.cs
  17. 8 4
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BasicShapeToolbar.cs
  18. 6 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BasicToolbar.cs
  19. 4 2
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BrightnessToolToolbar.cs
  20. 6 3
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/MagicWandToolbar.cs
  21. 2 1
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/PenToolbar.cs
  22. 6 7
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/SelectToolToolbar.cs
  23. 5 5
      src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/Toolbar.cs
  24. 6 0
      src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/PenToolViewModel.cs
  25. 1 1
      src/PixiEditor/Views/UserControls/EditableTextBlock.xaml.cs

+ 4 - 4
src/.editorconfig

@@ -47,7 +47,7 @@ dotnet_style_collection_initializer = true:suggestion
 dotnet_style_explicit_tuple_names = true:suggestion
 dotnet_style_null_propagation = true:suggestion
 dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
 dotnet_style_prefer_inferred_tuple_names = true:suggestion
 dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
 dotnet_style_prefer_auto_properties = true:silent
@@ -79,9 +79,9 @@ dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
 ###############################
 [*.cs]
 # var preferences
-csharp_style_var_for_built_in_types = true:silent
-csharp_style_var_when_type_is_apparent = true:silent
-csharp_style_var_elsewhere = true:silent
+csharp_style_var_for_built_in_types = false:suggestion
+csharp_style_var_when_type_is_apparent = false:silent
+csharp_style_var_elsewhere = false:suggestion
 # Expression-bodied members
 csharp_style_expression_bodied_methods = false:silent
 csharp_style_expression_bodied_constructors = false:silent

+ 10 - 0
src/PixiEditor/GlobalUsings.cs

@@ -0,0 +1,10 @@
+global using OneOf;
+global using OneOf.Types;
+global using PixiEditor.ChangeableDocument.Actions.Generated;
+global using PixiEditor.Helpers.Extensions;
+global using PixiEditor.Models.Commands.Attributes.Evaluators;
+global using PixiEditor.Models.Commands.Search;
+global using PixiEditor.ViewModels;
+global using PixiEditor.ViewModels.SubViewModels.Main;
+global using SkiaSharp;
+

+ 1 - 1
src/PixiEditor/Helpers/Behaviours/GlobalShortcutFocusBehavior.cs

@@ -27,6 +27,6 @@ internal class GlobalShortcutFocusBehavior : Behavior<FrameworkElement>
 
     private void AssociatedObject_GotKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
     {
-        ShortcutController.BlockShortcutExection("GlobalShortcutFocusBehavior");
+        ShortcutController.BlockShortcutExecution("GlobalShortcutFocusBehavior");
     }
 }

+ 1 - 1
src/PixiEditor/Models/Commands/CommandCollection.cs

@@ -1,8 +1,8 @@
 using System.Collections;
 using System.Diagnostics;
 using System.Windows.Input;
-using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.DataHolders;
+using Command = PixiEditor.Models.Commands.Commands.Command;
 
 namespace PixiEditor.Models.Commands;
 

+ 1 - 1
src/PixiEditor/Models/Controllers/ShortcutController.cs

@@ -16,7 +16,7 @@ internal class ShortcutController
 
     public Dictionary<KeyCombination, ToolViewModel> TransientShortcuts { get; set; } = new();
 
-    public static void BlockShortcutExection(string blocker)
+    public static void BlockShortcutExecution(string blocker)
     {
         if (_shortcutExecutionBlockers.Contains(blocker)) return;
         _shortcutExecutionBlockers.Add(blocker);

+ 55 - 67
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -1,69 +1,72 @@
 using System.Windows.Input;
 using ChunkyImageLib.DataHolders;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+using PixiEditor.ViewModels.SubViewModels.Document;
 
 namespace PixiEditor.Models.DocumentModels;
 #nullable enable
 internal class ChangeExecutionController
 {
-    public event EventHandler<VecI>? PixelMousePositionChanged;
-    public event EventHandler<(double, double)>? PreciseMousePositionChanged;
-    public event EventHandler<(Key, KeyStates)>? KeyStateChanged;
-
     public MouseButtonState LeftMouseState { get; private set; }
+    public VecI LastPixelPosition => lastPixelPos;
+    public VecD LastPrecisePosition => lastPrecisePos;
+    public bool IsChangeActive => currentSession is not null;
+
+    private readonly DocumentViewModel document;
+    private readonly DocumentHelpers helpers;
 
-    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;
+    private VecI lastPixelPos;
+    private VecD lastPrecisePos;
 
-    public VecI LastPixelPosition => new(lastPixelX, lastPixelY);
+    private UpdateableChangeExecutor? currentSession = null;
 
-    private int lastPixelX;
-    private int lastPixelY;
+    public ChangeExecutionController(DocumentViewModel document, DocumentHelpers helpers)
+    {
+        this.document = document;
+        this.helpers = helpers;
+    }
 
-    private Dictionary<Key, KeyStates> keyboardState = new();
-    //private ToolViewModel? currentTool = null;
-    //private UpdateableChangeSession? currentSession = null;
-    /*
-    private void TryStartToolSession(ToolViewModel tool, double mouseXOnCanvas, double mouseYOnCanvas)
+    public bool TryStartUpdateableChange<T>()
+        where T : UpdateableChangeExecutor, new()
     {
         if (currentSession is not null)
-            return;
-        currentSession = new(tool, mouseXOnCanvas, mouseYOnCanvas, keyboardState);
-        SessionStarted?.Invoke(this, currentSession);
+            return false;
+        T executor = new T();
+        executor.Initialize(document, helpers, this, EndChange);
+        if (executor.Start().IsT0)
+        {
+            currentSession = executor;
+            return true;
+        }
+        return false;
     }
 
-    private void TryStopToolSession()
+    private void EndChange(UpdateableChangeExecutor executor)
+    {
+        if (executor != currentSession)
+            throw new InvalidOperationException();
+        currentSession = null;
+    }
+
+    public bool TryStopActiveUpdateableChange()
     {
         if (currentSession is null)
-            return;
-        currentSession.EndSession(keyboardState);
-        SessionEnded?.Invoke(this, currentSession);
+            return false;
+        currentSession.ForceStop();
         currentSession = null;
+        return true;
     }
 
     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)
@@ -77,49 +80,34 @@ internal class ChangeExecutionController
         return key;
     }
 
-    public void ForceStopActiveSessionIfAny() => TryStopToolSession();
-    
-    public void OnToolChange(ToolViewModel tool)
-    {
-        currentTool = tool;
-        TryStopToolSession();
-    }
-
-    public void OnMouseMove(double newCanvasX, double newCanvasY)
+    public void OnMouseMove(VecD newCanvasPos)
     {
         //update internal state
-
-        var newX = (int)Math.Floor(newCanvasX);
-        var newY = (int)Math.Floor(newCanvasY);
-        var pixelPosChanged = false;
-        if (lastPixelX != newX || lastPixelY != newY)
+        VecI newPixelPos = (VecI)newCanvasPos.Floor();
+        bool pixelPosChanged = false;
+        if (lastPixelPos != newPixelPos)
         {
-            lastPixelX = newX;
-            lastPixelY = newY;
+            lastPixelPos = newPixelPos;
             pixelPosChanged = true;
         }
-
+        lastPrecisePos = newCanvasPos;
 
         //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 VecI(newX, newY)));
+        if (currentSession is not null)
+        {
+            if (pixelPosChanged)
+                currentSession.OnPixelPositionChange(newPixelPos);
+            currentSession.OnPrecisePositionChange(newCanvasPos);
+        }
     }
 
-    public void OnLeftMouseButtonDown(double canvasPosX, double canvasPosY)
+    public void OnLeftMouseButtonDown(VecD canvasPos)
     {
         //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);
+        //call session event
+        currentSession?.OnLeftMouseButtonDown(canvasPos);
     }
 
     public void OnLeftMouseButtonUp()
@@ -128,6 +116,6 @@ internal class ChangeExecutionController
         LeftMouseState = MouseButtonState.Released;
 
         //call session events
-        TryStopToolSession();
-    }*/
+        currentSession?.OnLeftMouseButtonUp();
+    }
 }

+ 2 - 0
src/PixiEditor/Models/DocumentModels/DocumentHelpers.cs

@@ -12,10 +12,12 @@ internal class DocumentHelpers
         Updater = new DocumentUpdater(doc, this);
         ActionAccumulator = new ActionAccumulator(doc, this);
         State = new DocumentState();
+        ChangeController = new ChangeExecutionController(doc, this);
     }
     public ActionAccumulator ActionAccumulator { get; }
     public DocumentChangeTracker Tracker { get; }
     public DocumentStructureHelper StructureHelper { get; }
     public DocumentUpdater Updater { get; }
     public DocumentState State { get; }
+    public ChangeExecutionController ChangeController { get; }
 }

+ 63 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineBasedPenExecutor.cs

@@ -0,0 +1,63 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ViewModels.SubViewModels.Document;
+using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
+using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+#nullable enable
+internal class LineBasedPenExecutor : UpdateableChangeExecutor
+{
+    private Guid guidValue;
+    private SKColor color;
+    private int toolSize;
+    private bool drawOnMask;
+
+    public override OneOf<Success, Error> Start()
+    {
+        ViewModelMain? vm = ViewModelMain.Current;
+        StructureMemberViewModel? member = document!.SelectedStructureMember;
+        PenToolViewModel? penTool = (PenToolViewModel?)(vm?.ToolsSubViewModel.GetTool<PenToolViewModel>());
+        PenToolbar? toolbar = penTool?.Toolbar as PenToolbar;
+        if (vm is null || penTool is null || member is null || toolbar is null)
+            return new Error();
+
+        guidValue = member.GuidValue;
+        color = vm.ColorsSubViewModel.PrimaryColor;
+        toolSize = toolbar.ToolSize;
+        drawOnMask = member.ShouldDrawOnMask;
+
+        LineBasedPen_Action? action = new(
+            guidValue,
+            color,
+            controller!.LastPixelPosition,
+            toolSize,
+            false,
+            drawOnMask);
+        helpers!.ActionAccumulator.AddActions(action);
+
+        return new Success();
+    }
+
+    public override void OnPixelPositionChange(VecI pos)
+    {
+        LineBasedPen_Action? action = new(
+            guidValue,
+            color,
+            pos,
+            toolSize,
+            false,
+            drawOnMask);
+        helpers!.ActionAccumulator.AddActions(action);
+    }
+
+    public override void OnLeftMouseButtonUp()
+    {
+        helpers!.ActionAccumulator.AddFinishedActions(new EndLineBasedPen_Action());
+        onEnded?.Invoke(this);
+    }
+
+    public override void ForceStop()
+    {
+        helpers!.ActionAccumulator.AddFinishedActions(new EndLineBasedPen_Action());
+    }
+}

+ 36 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs

@@ -0,0 +1,36 @@
+using System.Windows.Input;
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ViewModels.SubViewModels.Document;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+#nullable enable
+internal abstract class UpdateableChangeExecutor
+{
+    protected DocumentViewModel? document;
+    protected DocumentHelpers? helpers;
+    protected ChangeExecutionController? controller;
+    private bool initialized = false;
+
+    protected Action<UpdateableChangeExecutor>? onEnded;
+
+    public void Initialize(DocumentViewModel document, DocumentHelpers helpers, ChangeExecutionController controller, Action<UpdateableChangeExecutor> onEnded)
+    {
+        if (initialized)
+            throw new InvalidOperationException();
+        initialized = true;
+
+        this.document = document;
+        this.helpers = helpers;
+        this.controller = controller;
+        this.onEnded = onEnded;
+    }
+
+    public abstract OneOf<Success, Error> Start();
+    public abstract void ForceStop();
+    public virtual void OnPixelPositionChange(VecI pos) { }
+    public virtual void OnPrecisePositionChange(VecD pos) { }
+    public virtual void OnLeftMouseButtonDown(VecD pos) { }
+    public virtual void OnLeftMouseButtonUp() { }
+    public virtual void OnKeyDown(Key key) { }
+    public virtual void OnKeyUp(Key key) { }
+}

+ 19 - 72
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -3,14 +3,13 @@ using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.Operations;
-using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentModels;
+using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.Models.Position;
-using SkiaSharp;
 
 namespace PixiEditor.ViewModels.SubViewModels.Document;
 
@@ -82,7 +81,7 @@ internal class DocumentViewModel : NotifyableObject
         {
             if (ReferenceLayer is null)
                 return Matrix.Identity;
-            var skiaMatrix = OperationHelper.CreateMatrixFromPoints(ReferenceLayer.Shape, ReferenceLayer.Image.Size);
+            SKMatrix skiaMatrix = OperationHelper.CreateMatrixFromPoints(ReferenceLayer.Shape, ReferenceLayer.Image.Size);
             return new Matrix(skiaMatrix.ScaleX, skiaMatrix.SkewY, skiaMatrix.SkewX, skiaMatrix.ScaleY, skiaMatrix.TransX, skiaMatrix.TransY);
         }
     }
@@ -92,8 +91,6 @@ internal class DocumentViewModel : NotifyableObject
 
     private DocumentHelpers Helpers { get; }
 
-    private readonly DocumentManagerViewModel owner;
-
     private int verticalSymmetryAxisX;
 
     private bool horizontalSymmetryAxisEnabled;
@@ -108,9 +105,8 @@ internal class DocumentViewModel : NotifyableObject
 
     private SKPath selectionPath = new SKPath();
 
-    public DocumentViewModel(DocumentManagerViewModel owner, string name)
+    public DocumentViewModel(string name)
     {
-        this.owner = owner;
         //Name = name;
         TransformViewModel = new();
         //TransformViewModel.TransformMoved += OnTransformUpdate;
@@ -141,15 +137,15 @@ internal class DocumentViewModel : NotifyableObject
         TransformSelectionPathCommand = new RelayCommand(TransformSelectionPath);
         TransformSelectedAreaCommand = new RelayCommand(TransformSelectedArea);*/
 
-        foreach (var bitmap in Bitmaps)
+        foreach (KeyValuePair<ChunkResolution, WriteableBitmap> bitmap in Bitmaps)
         {
-            var surface = SKSurface.Create(
+            SKSurface? surface = SKSurface.Create(
                 new SKImageInfo(bitmap.Value.PixelWidth, bitmap.Value.PixelHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()),
                 bitmap.Value.BackBuffer, bitmap.Value.BackBufferStride);
             Surfaces[bitmap.Key] = surface;
         }
 
-        var previewSize = StructureMemberViewModel.CalculatePreviewSize(SizeBindable);
+        VecI previewSize = StructureMemberViewModel.CalculatePreviewSize(SizeBindable);
         PreviewBitmap = new WriteableBitmap(previewSize.X, previewSize.Y, 96, 96, PixelFormats.Pbgra32, null);
         PreviewSurface = SKSurface.Create(new SKImageInfo(previewSize.X, previewSize.Y, SKColorType.Bgra8888), PreviewBitmap.BackBuffer, PreviewBitmap.BackBufferStride);
 
@@ -194,7 +190,7 @@ internal class DocumentViewModel : NotifyableObject
 
     public void InternalUpdateSelectionPath(SKPath selectionPath)
     {
-        (var toDispose, this.selectionPath) = (this.selectionPath, selectionPath);
+        (SKPath? toDispose, this.selectionPath) = (this.selectionPath, selectionPath);
         toDispose.Dispose();
         RaisePropertyChanged(nameof(SelectionPathBindable));
     }
@@ -211,77 +207,28 @@ internal class DocumentViewModel : NotifyableObject
 
     #endregion
 
-    public void AddOrUpdateViewport(ViewportInfo info)
-    {
-        Helpers.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(info));
-    }
+    public void AddOrUpdateViewport(ViewportInfo info) => Helpers.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(info));
 
-    public void RemoveViewport(Guid viewportGuid)
-    {
-        Helpers.ActionAccumulator.AddActions(new RemoveViewport_PassthroughAction(viewportGuid));
-    }
+    public void RemoveViewport(Guid viewportGuid) => Helpers.ActionAccumulator.AddActions(new RemoveViewport_PassthroughAction(viewportGuid));
 
-    public void CreateStructureMember(StructureMemberType type)
-    {
-        Helpers.StructureHelper.CreateNewStructureMember(type);
-    }
+    public void CreateStructureMember(StructureMemberType type) => Helpers.StructureHelper.CreateNewStructureMember(type);
 
-    public void SetSelectedMember(Guid memberGuid)
-    {
-        Helpers.ActionAccumulator.AddActions(new SetSelectedMember_PassthroughAction(memberGuid));
-    }
+    public void SetSelectedMember(Guid memberGuid) => Helpers.ActionAccumulator.AddActions(new SetSelectedMember_PassthroughAction(memberGuid));
 
-    public void AddSoftSelectedMember(Guid memberGuid)
-    {
-        Helpers.ActionAccumulator.AddActions(new AddSoftSelectedMember_PassthroughAction(memberGuid));
-    }
+    public void AddSoftSelectedMember(Guid memberGuid) => Helpers.ActionAccumulator.AddActions(new AddSoftSelectedMember_PassthroughAction(memberGuid));
 
-    public void ClearSoftSelectedMembers()
-    {
-        Helpers.ActionAccumulator.AddActions(new ClearSoftSelectedMembers_PassthroughAction());
-    }
+    public void ClearSoftSelectedMembers() => Helpers.ActionAccumulator.AddActions(new ClearSoftSelectedMembers_PassthroughAction());
 
-    public void OnKeyDown(Key args)
-    {
+    public void UsePenTool() => Helpers.ChangeController.TryStartUpdateableChange<LineBasedPenExecutor>();
 
-    }
 
-    public void OnKeyUp(Key args)
-    {
+    public void OnKeyDown(Key args) => Helpers.ChangeController.OnKeyDown(args);
 
-    }
+    public void OnKeyUp(Key args) => Helpers.ChangeController.OnKeyUp(args);
 
-    private StructureMemberViewModel? drawingTarget = null;
-    public void OnCanvasLeftMouseButtonDown(VecD pos)
-    {
-        if (SelectedStructureMember is null)
-            return;
-        drawingTarget = SelectedStructureMember;
-        Helpers.ActionAccumulator.AddActions(new LineBasedPen_Action(
-            drawingTarget.GuidValue,
-            SKColors.Black,
-            (VecI)pos,
-            (int)1,
-            false,
-            false));
-    }
+    public void OnCanvasLeftMouseButtonDown(VecD pos) => Helpers.ChangeController.OnLeftMouseButtonDown(pos);
 
-    public void OnCanvasMouseMove(VecD newPos)
-    {
-        if (drawingTarget is null)
-            return;
-        Helpers.ActionAccumulator.AddActions(new LineBasedPen_Action(
-            drawingTarget.GuidValue,
-            SKColors.Black,
-            (VecI)newPos,
-            (int)1,
-            false,
-            false));
-    }
+    public void OnCanvasMouseMove(VecD newPos) => Helpers.ChangeController.OnMouseMove(newPos);
 
-    public void OnCanvasLeftMouseButtonUp()
-    {
-        drawingTarget = null;
-        Helpers.ActionAccumulator.AddFinishedActions(new EndLineBasedPen_Action());
-    }
+    public void OnCanvasLeftMouseButtonUp() => Helpers.ChangeController.OnLeftMouseButtonUp();
 }

+ 0 - 5
src/PixiEditor/ViewModels/SubViewModels/Main/ClipboardViewModel.cs

@@ -2,13 +2,8 @@
 using System.Windows;
 using System.Windows.Input;
 using System.Windows.Media;
-using PixiEditor.Helpers.Extensions;
-using PixiEditor.Models.Commands.Attributes;
 using PixiEditor.Models.Commands.Attributes.Commands;
-using PixiEditor.Models.Commands.Attributes.Evaluators;
-using PixiEditor.Models.Commands.Search;
 using PixiEditor.Models.Controllers;
-using SkiaSharp;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main;
 

+ 4 - 4
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -69,7 +69,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
 
     public void NewDocument(int width, int height, bool addBaseLayer = true)
     {
-        Owner.DocumentManagerSubViewModel.Documents.Add(new DocumentViewModel(Owner.DocumentManagerSubViewModel, "Unnamed"));
+        Owner.DocumentManagerSubViewModel.Documents.Add(new DocumentViewModel("Unnamed"));
         Owner.DocumentManagerSubViewModel.ActiveDocument = Owner.DocumentManagerSubViewModel.Documents[^1];
         /*
         if (addBaseLayer)
@@ -271,11 +271,11 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             return;
         }
 
-        var recentlyOpenedDocuments = new List<RecentlyOpenedDocument>(RecentlyOpened.Take(newAmount));
+        List<RecentlyOpenedDocument> recentlyOpenedDocuments = new List<RecentlyOpenedDocument>(RecentlyOpened.Take(newAmount));
 
         RecentlyOpened.Clear();
 
-        foreach (var recent in recentlyOpenedDocuments)
+        foreach (RecentlyOpenedDocument recent in recentlyOpenedDocuments)
         {
             RecentlyOpened.Add(recent);
         }
@@ -283,7 +283,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
 
     private List<RecentlyOpenedDocument> GetRecentlyOpenedDocuments()
     {
-        var paths = IPreferences.Current.GetLocalPreference(nameof(RecentlyOpened), new JArray()).ToObject<string[]>()
+        IEnumerable<string> paths = IPreferences.Current.GetLocalPreference(nameof(RecentlyOpened), new JArray()).ToObject<string[]>()
             .Take(IPreferences.Current.GetPreference("MaxOpenedRecently", 8));
 
         List<RecentlyOpenedDocument> documents = new List<RecentlyOpenedDocument>();

+ 11 - 11
src/PixiEditor/ViewModels/SubViewModels/Main/IoViewModel.cs

@@ -3,7 +3,6 @@ using System.Windows.Input;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Commands;
-using PixiEditor.Models.Commands.Commands;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.Events;
@@ -69,7 +68,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
 
     private void OnKeyDown(KeyEventArgs args)
     {
-        var key = args.Key;
+        Key key = args.Key;
         if (key == Key.System)
             key = args.SystemKey;
 
@@ -89,7 +88,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
             return;
         }
 
-        var controller = Owner.ShortcutController;
+        ShortcutController controller = Owner.ShortcutController;
 
         Key finalKey = args.Key;
         if (finalKey == Key.System)
@@ -97,8 +96,8 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
             finalKey = args.SystemKey;
         }
 
-        Command.ToolCommand tool = CommandController.Current.Commands
-            .Select(x => x as Command.ToolCommand)
+        Models.Commands.Commands.Command.ToolCommand tool = CommandController.Current.Commands
+            .Select(x => x as Models.Commands.Commands.Command.ToolCommand)
             .FirstOrDefault(x => x != null && x.TransientKey == finalKey);
 
         if (tool != null)
@@ -110,10 +109,10 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
     private void ProcessShortcutDown(bool isRepeat, Key key)
     {
         if (isRepeat && !restoreToolOnKeyUp && Owner.ShortcutController.LastCommands != null &&
-            Owner.ShortcutController.LastCommands.Any(x => x is Command.ToolCommand))
+            Owner.ShortcutController.LastCommands.Any(x => x is Models.Commands.Commands.Command.ToolCommand))
         {
             restoreToolOnKeyUp = true;
-            ShortcutController.BlockShortcutExection("ShortcutDown");
+            ShortcutController.BlockShortcutExecution("ShortcutDown");
         }
 
         Owner.ShortcutController.KeyPressed(key, Keyboard.Modifiers);
@@ -121,7 +120,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
 
     private void OnKeyUp(KeyEventArgs args)
     {
-        var key = args.Key;
+        Key key = args.Key;
         if (key == Key.System)
             key = args.SystemKey;
 
@@ -150,12 +149,13 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
         if (args.Button == MouseButton.Left)
         {
             DocumentManagerViewModel docManager = Owner.DocumentManagerSubViewModel;
-            var activeDocument = docManager.ActiveDocument;
+            DocumentViewModel activeDocument = docManager.ActiveDocument;
             if (activeDocument == null)
                 return;
 
             //docManager.InputTarget.OnLeftMouseButtonDown(activeDocument.MouseXOnCanvas, activeDocument.MouseYOnCanvas);
             docManager.ActiveDocument.OnCanvasLeftMouseButtonDown(args.PositionOnCanvas);
+            Owner.ToolsSubViewModel.OnLeftMouseButtonDown(args.PositionOnCanvas);
         }
     }
 
@@ -174,7 +174,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
     {
         if (setOn)
         {
-            var transientToolIsActive = Owner.ToolsSubViewModel.ActiveTool.GetType() == type;
+            bool transientToolIsActive = Owner.ToolsSubViewModel.ActiveTool.GetType() == type;
             if (!transientToolIsActive)
             {
                 Owner.ToolsSubViewModel.SetActiveTool(type);
@@ -191,7 +191,7 @@ internal class IoViewModel : SubViewModel<ViewModelMain>
 
     private void OnMouseMove(object sender, VecD pos)
     {
-        var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        DocumentViewModel activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (activeDocument is null)
             return;
         //Owner.DocumentManagerSubViewModel.InputTarget.OnMouseMove(activeDocument.MouseXOnCanvas, activeDocument.MouseYOnCanvas);

+ 18 - 20
src/PixiEditor/ViewModels/SubViewModels/Main/ToolsViewModel.cs

@@ -1,11 +1,12 @@
 using System.Windows.Input;
+using ChunkyImageLib.DataHolders;
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.UserPreferences;
 using PixiEditor.ViewModels.SubViewModels.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
-using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
+using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 namespace PixiEditor.ViewModels.SubViewModels.Main;
 
@@ -30,20 +31,6 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         private set => SetProperty(ref activeTool, value);
     }
 
-    public int ToolSize
-    {
-        get => ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize") != null
-            ? ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize").Value
-            : 1;
-        set
-        {
-            if (ActiveTool.Toolbar.GetSetting<SizeSetting>("ToolSize") is SizeSetting toolSize)
-            {
-                toolSize.Value = value;
-            }
-        }
-    }
-
     public List<ToolViewModel> ToolSet { get; private set; }
 
     public event EventHandler<SelectedToolEventArgs> SelectedToolChanged;
@@ -64,12 +51,18 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
 
     public void SetupToolsTooltipShortcuts(IServiceProvider services)
     {
-        foreach (var tool in ToolSet)
+        foreach (ToolViewModel tool in ToolSet)
         {
             tool.Shortcut = Owner.ShortcutController.GetToolShortcut(tool.GetType());
         }
     }
 
+    public ToolViewModel? GetTool<T>()
+        where T : ToolViewModel
+    {
+        return ToolSet?.Where(static tool => tool is T).FirstOrDefault();
+    }
+
     public void SetActiveTool<T>()
         where T : ToolViewModel
     {
@@ -134,11 +127,11 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.Tools.DecreaseSize", -1, "Decrease Tool Size", "Decrease Tool Size", Key = Key.OemOpenBrackets)]
     public void ChangeToolSize(int increment)
     {
-        int newSize = ToolSize + increment;
+        if (ActiveTool?.Toolbar is not BasicToolbar toolbar)
+            return;
+        int newSize = toolbar.ToolSize + increment;
         if (newSize > 0)
-        {
-            ToolSize = newSize;
-        }
+            toolbar.ToolSize = newSize;
     }
 
     public void SetActiveTool(Type toolType)
@@ -160,6 +153,11 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>
         }
     }
 
+    public void OnLeftMouseButtonDown(VecD canvasPos)
+    {
+        ActiveTool?.OnLeftMouseButtonDown(canvasPos);
+    }
+
     public void OnKeyDown(Key key)
     {
         bool shiftIsDown = key is Key.LeftShift or Key.RightShift;

+ 2 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/Tool.cs

@@ -1,5 +1,5 @@
 using System.Windows.Input;
-using PixiEditor.Helpers.Extensions;
+using ChunkyImageLib.DataHolders;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
@@ -46,4 +46,5 @@ internal abstract class ToolViewModel : NotifyableObject
     public Toolbar Toolbar { get; set; } = new EmptyToolbar();
 
     public virtual void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown) { }
+    public virtual void OnLeftMouseButtonDown(VecD pos) { }
 }

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Settings/SizeSetting.cs

@@ -16,7 +16,7 @@ internal class SizeSetting : Setting<int>
 
     private SizeInput GenerateTextBox()
     {
-        var tb = new SizeInput
+        SizeInput tb = new SizeInput
         {
             Width = 65,
             Height = 20,
@@ -25,7 +25,7 @@ internal class SizeSetting : Setting<int>
             IsEnabled = true
         };
 
-        var binding = new Binding("Value")
+        Binding binding = new Binding("Value")
         {
             Mode = BindingMode.TwoWay,
         };

+ 8 - 4
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BasicShapeToolbar.cs

@@ -1,12 +1,16 @@
-using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
+using System.Windows.Media;
+using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
-
+#nullable enable
 internal class BasicShapeToolbar : BasicToolbar
 {
+    public bool Fill => GetSetting<BoolSetting>(nameof(Fill)).Value;
+    public Color FillColor => GetSetting<ColorSetting>(nameof(FillColor)).Value;
+
     public BasicShapeToolbar()
     {
-        Settings.Add(new BoolSetting("Fill", "Fill shape: "));
-        Settings.Add(new ColorSetting("FillColor", "Fill color"));
+        Settings.Add(new BoolSetting(nameof(Fill), "Fill shape: "));
+        Settings.Add(new ColorSetting(nameof(FillColor), "Fill color"));
     }
 }

+ 6 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BasicToolbar.cs

@@ -7,8 +7,13 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 /// </summary>
 internal class BasicToolbar : Toolbar
 {
+    public int ToolSize
+    {
+        get => GetSetting<SizeSetting>(nameof(ToolSize)).Value;
+        set => GetSetting<SizeSetting>(nameof(ToolSize)).Value = value;
+    }
     public BasicToolbar()
     {
-        Settings.Add(new SizeSetting("ToolSize", "Tool size:"));
+        Settings.Add(new SizeSetting(nameof(ToolSize), "Tool size:"));
     }
 }

+ 4 - 2
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/BrightnessToolToolbar.cs

@@ -5,9 +5,11 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 internal class BrightnessToolToolbar : BasicToolbar
 {
+    public float CorrectionFactor => GetSetting<FloatSetting>(nameof(CorrectionFactor)).Value;
+    public BrightnessMode BrightnessMode => GetSetting<EnumSetting<BrightnessMode>>(nameof(BrightnessMode)).Value;
     public BrightnessToolToolbar(float initialValue)
     {
-        Settings.Add(new FloatSetting("CorrectionFactor", initialValue, "Strength:", 0f, 100f));
-        Settings.Add(new EnumSetting<BrightnessMode>("BrightnessMode", "Mode"));
+        Settings.Add(new FloatSetting(nameof(CorrectionFactor), initialValue, "Strength:", 0f, 100f));
+        Settings.Add(new EnumSetting<BrightnessMode>(nameof(BrightnessMode), "Mode"));
     }
 }

+ 6 - 3
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/MagicWandToolbar.cs

@@ -1,13 +1,16 @@
-using PixiEditor.Models.Enums;
+using System.Windows.Controls;
+using PixiEditor.Models.Enums;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Settings;
 
 namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
-internal class MagicWandToolbar : SelectToolToolbar
+internal class MagicWandToolbar : BasicToolbar
 {
+    public SelectionMode SelectMode => GetSetting<EnumSetting<SelectionMode>>(nameof(SelectMode)).Value;
+    public DocumentScope DocumentScope => GetSetting<EnumSetting<DocumentScope>>(nameof(DocumentScope)).Value;
     public MagicWandToolbar()
-        : base(false)
     {
+        Settings.Add(new EnumSetting<SelectionMode>(nameof(SelectMode), "Selection type"));
         Settings.Add(new EnumSetting<DocumentScope>(nameof(DocumentScope), "Scope"));
     }
 }

+ 2 - 1
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/PenToolbar.cs

@@ -4,8 +4,9 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 internal class PenToolbar : BasicToolbar
 {
+    public bool PixelPerfectEnabled => GetSetting<BoolSetting>(nameof(PixelPerfectEnabled)).Value;
     public PenToolbar()
     {
-        Settings.Add(new BoolSetting("PixelPerfectEnabled", "Pixel perfect"));
+        Settings.Add(new BoolSetting(nameof(PixelPerfectEnabled), "Pixel perfect"));
     }
 }

+ 6 - 7
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/SelectToolToolbar.cs

@@ -6,13 +6,12 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 internal class SelectToolToolbar : Toolbar
 {
-    public SelectToolToolbar(bool includeSelectionShape = true)
-    {
-        Settings.Add(new EnumSetting<SelectionMode>("SelectMode", "Selection type"));
+    public SelectionMode SelectMode => GetSetting<EnumSetting<SelectionMode>>(nameof(SelectMode)).Value;
+    public SelectionShape SelectShape => GetSetting<EnumSetting<SelectionShape>>(nameof(SelectShape)).Value;
 
-        if (includeSelectionShape)
-        {
-            Settings.Add(new EnumSetting<SelectionShape>("SelectShape", "Selection shape"));
-        }
+    public SelectToolToolbar()
+    {
+        Settings.Add(new EnumSetting<SelectionMode>(nameof(SelectMode), "Selection type"));
+        Settings.Add(new EnumSetting<SelectionShape>(nameof(SelectShape), "Selection shape"));
     }
 }

+ 5 - 5
src/PixiEditor/ViewModels/SubViewModels/Tools/ToolSettings/Toolbars/Toolbar.cs

@@ -12,7 +12,7 @@ internal abstract class Toolbar
 
     public void GenerateSettings()
     {
-        foreach (var setting in Settings)
+        foreach (Setting setting in Settings)
         {
             setting.SettingControl = setting.GenerateControl();
         }
@@ -38,9 +38,9 @@ internal abstract class Toolbar
     public T GetSetting<T>(string name)
         where T : Setting
     {
-        var setting = Settings.FirstOrDefault(currentSetting => string.Equals(currentSetting.Name, name, StringComparison.CurrentCultureIgnoreCase));
+        Setting setting = Settings.FirstOrDefault(currentSetting => string.Equals(currentSetting.Name, name, StringComparison.CurrentCultureIgnoreCase));
 
-        if (setting == null || !(setting is T convertedSetting))
+        if (setting is null || setting is not T convertedSetting)
         {
             return null;
         }
@@ -53,7 +53,7 @@ internal abstract class Toolbar
     /// </summary>
     public void SaveToolbarSettings()
     {
-        for (var i = 0; i < Settings.Count; i++)
+        for (int i = 0; i < Settings.Count; i++)
         {
             if (SharedSettings.Any(x => x.Name == Settings[i].Name))
             {
@@ -71,7 +71,7 @@ internal abstract class Toolbar
     /// </summary>
     public void LoadSharedSettings()
     {
-        for (var i = 0; i < SharedSettings.Count; i++)
+        for (int i = 0; i < SharedSettings.Count; i++)
         {
             if (Settings.Any(x => x.Name == SharedSettings[i].Name))
             {

+ 6 - 0
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/PenToolViewModel.cs

@@ -1,4 +1,5 @@
 using System.Windows.Input;
+using ChunkyImageLib.DataHolders;
 using PixiEditor.Models.Commands.Attributes.Commands;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
@@ -15,5 +16,10 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools
         }
 
         public override string Tooltip => $"Standard brush. ({Shortcut})";
+
+        public override void OnLeftMouseButtonDown(VecD pos)
+        {
+            ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.UsePenTool();
+        }
     }
 }

+ 1 - 1
src/PixiEditor/Views/UserControls/EditableTextBlock.xaml.cs

@@ -70,7 +70,7 @@ internal partial class EditableTextBlock : UserControl
 
     public void EnableEditing()
     {
-        ShortcutController.BlockShortcutExection("EditableTextBlock");
+        ShortcutController.BlockShortcutExecution("EditableTextBlock");
         TextBlockVisibility = Visibility.Hidden;
         IsEditing = true;
         Dispatcher.BeginInvoke(