Browse Source

Made shapes react to changes live

flabbet 11 months ago
parent
commit
ccf28cea4f
34 changed files with 484 additions and 162 deletions
  1. 2 0
      src/ChunkyImageLib/Operations/EllipseOperation.cs
  2. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  3. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
  4. 9 6
      src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterEllipse_UpdateableChange.cs
  5. 13 2
      src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelected_UpdateableChange.cs
  6. 2 2
      src/PixiEditor/Data/Configs/ToolSetsConfig.json
  7. 4 1
      src/PixiEditor/Data/Localization/Languages/en.json
  8. 2 1
      src/PixiEditor/Helpers/ServiceCollectionHelpers.cs
  9. 1 0
      src/PixiEditor/Models/Config/ConfigManager.cs
  10. 17 1
      src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs
  11. 4 0
      src/PixiEditor/Models/DocumentModels/Public/DocumentEventsModule.cs
  12. 9 3
      src/PixiEditor/Models/DocumentModels/Public/DocumentToolsModule.cs
  13. 0 37
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EllipseToolExecutor.cs
  14. 25 8
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineToolExecutor.cs
  15. 51 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs
  16. 49 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs
  17. 0 33
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RectangleToolExecutor.cs
  18. 35 16
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ShapeToolExecutor.cs
  19. 4 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs
  20. 29 0
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs
  21. 4 2
      src/PixiEditor/Models/Handlers/Toolbars/IBasicShapeToolbar.cs
  22. 4 1
      src/PixiEditor/Models/Handlers/Toolbars/IToolbar.cs
  23. 1 1
      src/PixiEditor/Models/Handlers/Tools/IRasterEllipseToolHandler.cs
  24. 6 0
      src/PixiEditor/Models/Handlers/Tools/IVectorEllipseToolHandler.cs
  25. 1 1
      src/PixiEditor/PixiEditor.csproj
  26. 91 34
      src/PixiEditor/ViewModels/SubViewModels/ColorsViewModel.cs
  27. 20 1
      src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs
  28. 51 4
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicShapeToolbar.cs
  29. 1 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicToolbar.cs
  30. 14 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/Toolbar.cs
  31. 1 1
      src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/ToolbarFactory.cs
  32. 3 3
      src/PixiEditor/ViewModels/Tools/Tools/RasterEllipseToolViewModel.cs
  33. 27 0
      src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs
  34. 2 0
      src/PixiEditor/Views/Input/ToolSettingColorPicker.axaml.cs

+ 2 - 0
src/ChunkyImageLib/Operations/EllipseOperation.cs

@@ -119,11 +119,13 @@ internal class EllipseOperation : IMirroredDrawOperation
             if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
             if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
             {
             {
                 surf.Canvas.Save();
                 surf.Canvas.Save();
+                surf.Canvas.RotateRadians((float)rotation, (float)location.Center.X, (float)location.Center.Y);
                 surf.Canvas.ClipPath(innerPath!);
                 surf.Canvas.ClipPath(innerPath!);
                 surf.Canvas.DrawColor(fillColor, paint.BlendMode);
                 surf.Canvas.DrawColor(fillColor, paint.BlendMode);
                 surf.Canvas.Restore();
                 surf.Canvas.Restore();
             }
             }
             surf.Canvas.Save();
             surf.Canvas.Save();
+            surf.Canvas.RotateRadians((float)rotation, (float)location.Center.X, (float)location.Center.Y);
             surf.Canvas.ClipPath(outerPath!);
             surf.Canvas.ClipPath(outerPath!);
             surf.Canvas.ClipPath(innerPath!, ClipOperation.Difference);
             surf.Canvas.ClipPath(innerPath!, ClipOperation.Difference);
             surf.Canvas.DrawColor(strokeColor, paint.BlendMode);
             surf.Canvas.DrawColor(strokeColor, paint.BlendMode);

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -53,7 +53,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
 
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
     {
     {
-        return (RectD)GetLayerImageAtFrame(frameTime.Frame).FindTightCommittedBounds();
+        return (RectD?)GetLayerImageAtFrame(frameTime.Frame).FindTightCommittedBounds();
     }
     }
 
 
     protected override Texture? OnExecute(RenderingContext context)
     protected override Texture? OnExecute(RenderingContext context)

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs

@@ -26,7 +26,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject
     
     
     protected override Texture? OnExecute(RenderingContext context)
     protected override Texture? OnExecute(RenderingContext context)
     {
     {
-        Texture texture = RequestTexture(0, (VecI)(context.DocumentSize /** context.ChunkResolution.Multiplier()*/));
+        Texture texture = RequestTexture(0, context.DocumentSize);
 
 
         ShapeData.Rasterize(texture.DrawingSurface, context.ChunkResolution);
         ShapeData.Rasterize(texture.DrawingSurface, context.ChunkResolution);
         
         

+ 9 - 6
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawEllipse_UpdateableChange.cs → src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterEllipse_UpdateableChange.cs

@@ -3,21 +3,21 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Numerics;
 using PixiEditor.Numerics;
 
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
-internal class DrawEllipse_UpdateableChange : UpdateableChange
+internal class DrawRasterEllipse_UpdateableChange : UpdateableChange
 {
 {
     private readonly Guid memberGuid;
     private readonly Guid memberGuid;
     private RectI location;
     private RectI location;
     private double rotation;
     private double rotation;
-    private readonly Color strokeColor;
-    private readonly Color fillColor;
-    private readonly int strokeWidth;
+    private Color strokeColor;
+    private Color fillColor;
+    private int strokeWidth;
     private readonly bool drawOnMask;
     private readonly bool drawOnMask;
     private int frame;
     private int frame;
 
 
     private CommittedChunkStorage? storedChunks;
     private CommittedChunkStorage? storedChunks;
 
 
     [GenerateUpdateableChangeActions]
     [GenerateUpdateableChangeActions]
-    public DrawEllipse_UpdateableChange(Guid memberGuid, RectI location, double rotationRad, Color strokeColor, Color fillColor, int strokeWidth, bool drawOnMask, int frame)
+    public DrawRasterEllipse_UpdateableChange(Guid memberGuid, RectI location, double rotationRad, Color strokeColor, Color fillColor, int strokeWidth, bool drawOnMask, int frame)
     {
     {
         this.memberGuid = memberGuid;
         this.memberGuid = memberGuid;
         this.location = location;
         this.location = location;
@@ -30,10 +30,13 @@ internal class DrawEllipse_UpdateableChange : UpdateableChange
     }
     }
 
 
     [UpdateChangeMethod]
     [UpdateChangeMethod]
-    public void Update(RectI location, double rotationRad)
+    public void Update(RectI location, double rotationRad, Color strokeColor, Color fillColor, int strokeWidth)
     {
     {
         this.location = location;
         this.location = location;
         rotation = rotationRad;
         rotation = rotationRad;
+        this.strokeColor = strokeColor;
+        this.fillColor = fillColor;
+        this.strokeWidth = strokeWidth;
     }
     }
 
 
     public override bool InitializeAndValidate(Document target)
     public override bool InitializeAndValidate(Document target)

+ 13 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelected_UpdateableChange.cs

@@ -77,7 +77,7 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
         }
         }
 
 
         StructureNode firstLayer = target.FindMemberOrThrow(memberData[0].MemberId);
         StructureNode firstLayer = target.FindMemberOrThrow(memberData[0].MemberId);
-        RectD tightBounds = firstLayer.GetTightBounds(frame).Value;
+        RectD tightBounds = firstLayer.GetTightBounds(frame) ?? default;
 
 
         if (memberData.Count == 1 && firstLayer is VectorLayerNode vectorLayer)
         if (memberData.Count == 1 && firstLayer is VectorLayerNode vectorLayer)
         {
         {
@@ -87,7 +87,18 @@ internal class TransformSelected_UpdateableChange : UpdateableChange
         for (var i = 1; i < memberData.Count; i++)
         for (var i = 1; i < memberData.Count; i++)
         {
         {
             StructureNode layer = target.FindMemberOrThrow(memberData[i].MemberId);
             StructureNode layer = target.FindMemberOrThrow(memberData[i].MemberId);
-            tightBounds = tightBounds.Union((RectD)layer.GetTightBounds(frame).Value);
+            
+            var layerTightBounds = layer.GetTightBounds(frame);
+            
+            if (tightBounds == default)
+            {
+                tightBounds = layerTightBounds.GetValueOrDefault();
+            }
+
+            if (layerTightBounds is not null)
+            {
+                tightBounds = tightBounds.Union(layerTightBounds.Value);
+            }
         }
         }
 
 
         tightBoundsSize = tightBounds.Size;
         tightBoundsSize = tightBounds.Size;

+ 2 - 2
src/PixiEditor/Data/Configs/ToolSetsConfig.json

@@ -11,7 +11,7 @@
       "Lasso",
       "Lasso",
       "FloodFill",
       "FloodFill",
       "Line",
       "Line",
-      "Ellipse",
+      "RasterEllipse",
       "Rectangle",
       "Rectangle",
       "Eraser",
       "Eraser",
       "ColorPicker",
       "ColorPicker",
@@ -26,7 +26,7 @@
       "RotateViewport",
       "RotateViewport",
       "Move",
       "Move",
       "Line",
       "Line",
-      "Ellipse",
+      "VectorEllipse",
       "Rectangle"
       "Rectangle"
     ]
     ]
   }
   }

+ 4 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -747,5 +747,8 @@
   "COS": "Cos",
   "COS": "Cos",
   "TAN": "Tan",
   "TAN": "Tan",
   "PIXEL_ART_TOOLSET": "Pixel Art",
   "PIXEL_ART_TOOLSET": "Pixel Art",
-  "VECTOR_TOOLSET": "Vector"
+  "VECTOR_TOOLSET": "Vector",
+  "VECTOR_LAYER": "Vector Layer",
+  "STROKE_COLOR_LABEL": "Stroke",
+  "SYNC_WITH_PRIMARY_COLOR_LABEL": "Sync with primary color"
 }
 }

+ 2 - 1
src/PixiEditor/Helpers/ServiceCollectionHelpers.cs

@@ -90,11 +90,12 @@ internal static class ServiceCollectionHelpers
             .AddTool<ILassoToolHandler, LassoToolViewModel>()
             .AddTool<ILassoToolHandler, LassoToolViewModel>()
             .AddTool<IFloodFillToolHandler, FloodFillToolViewModel>()
             .AddTool<IFloodFillToolHandler, FloodFillToolViewModel>()
             .AddTool<ILineToolHandler, LineToolViewModel>()
             .AddTool<ILineToolHandler, LineToolViewModel>()
-            .AddTool<IEllipseToolHandler, EllipseToolViewModel>()
+            .AddTool<IRasterEllipseToolHandler, RasterEllipseToolViewModel>()
             .AddTool<IRectangleToolHandler, RectangleToolViewModel>()
             .AddTool<IRectangleToolHandler, RectangleToolViewModel>()
             .AddTool<IEraserToolHandler, EraserToolViewModel>()
             .AddTool<IEraserToolHandler, EraserToolViewModel>()
             .AddTool<IColorPickerHandler, ColorPickerToolViewModel>()
             .AddTool<IColorPickerHandler, ColorPickerToolViewModel>()
             .AddTool<IBrightnessToolHandler, BrightnessToolViewModel>()
             .AddTool<IBrightnessToolHandler, BrightnessToolViewModel>()
+            .AddTool<IVectorEllipseToolHandler, VectorEllipseToolViewModel>()
             .AddTool<ZoomToolViewModel>()
             .AddTool<ZoomToolViewModel>()
             // File types
             // File types
             .AddSingleton<IoFileType, PixiFileType>()
             .AddSingleton<IoFileType, PixiFileType>()

+ 1 - 0
src/PixiEditor/Models/Config/ConfigManager.cs

@@ -7,6 +7,7 @@ namespace PixiEditor.Models.Config;
 
 
 public class ConfigManager
 public class ConfigManager
 {
 {
+    // TODO: Copy config to local folder so it can be modified
     public T GetConfig<T>(string configName)
     public T GetConfig<T>(string configName)
     {
     {
         string path = $"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Data/Configs/{configName}.json";
         string path = $"avares://{Assembly.GetExecutingAssembly().GetName().Name}/Data/Configs/{configName}.json";

+ 17 - 1
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -1,6 +1,7 @@
 using Avalonia.Input;
 using Avalonia.Input;
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
@@ -188,8 +189,13 @@ internal class ChangeExecutionController
         LastTransformState = corners;
         LastTransformState = corners;
         currentSession?.OnTransformMoved(corners);
         currentSession?.OnTransformMoved(corners);
     }
     }
-
+    
     public void TransformAppliedInlet() => currentSession?.OnTransformApplied();
     public void TransformAppliedInlet() => currentSession?.OnTransformApplied();
+    
+    public void SettingsChangedInlet(string name, object value)
+    {
+        currentSession?.OnSettingsChanged(name, value);
+    }
 
 
     public void LineOverlayMovedInlet(VecD start, VecD end)
     public void LineOverlayMovedInlet(VecD start, VecD end)
     {
     {
@@ -200,4 +206,14 @@ internal class ChangeExecutionController
     {
     {
         currentSession?.OnSelectedObjectNudged(distance);
         currentSession?.OnSelectedObjectNudged(distance);
     }
     }
+
+    public void PrimaryColorChangedInlet(Color color)
+    {
+        currentSession?.OnColorChanged(color, true);
+    }
+
+    public void SecondaryColorChangedInlet(Color color)
+    {
+        currentSession?.OnColorChanged(color, false);
+    }
 }
 }

+ 4 - 0
src/PixiEditor/Models/DocumentModels/Public/DocumentEventsModule.cs

@@ -1,5 +1,6 @@
 using Avalonia.Input;
 using Avalonia.Input;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
@@ -43,6 +44,9 @@ internal class DocumentEventsModule
     public void OnOpacitySliderDragged(float newValue) => Internals.ChangeController.OpacitySliderDraggedInlet(newValue);
     public void OnOpacitySliderDragged(float newValue) => Internals.ChangeController.OpacitySliderDraggedInlet(newValue);
     public void OnOpacitySliderDragEnded() => Internals.ChangeController.OpacitySliderDragEndedInlet();
     public void OnOpacitySliderDragEnded() => Internals.ChangeController.OpacitySliderDragEndedInlet();
     public void OnApplyTransform() => Internals.ChangeController.TransformAppliedInlet();
     public void OnApplyTransform() => Internals.ChangeController.TransformAppliedInlet();
+    public void SettingsChanged(string name, object value) => Internals.ChangeController.SettingsChangedInlet(name, value);
+    public void PrimaryColorChanged(Color color) => Internals.ChangeController.PrimaryColorChangedInlet(color);
+    public void SecondaryColorChanged(Color color) => Internals.ChangeController.SecondaryColorChangedInlet(color);
     public void OnSymmetryDragStarted(SymmetryAxisDirection dir) => Internals.ChangeController.SymmetryDragStartedInlet(dir);
     public void OnSymmetryDragStarted(SymmetryAxisDirection dir) => Internals.ChangeController.SymmetryDragStartedInlet(dir);
     public void OnSymmetryDragged(SymmetryAxisDragInfo info) => Internals.ChangeController.SymmetryDraggedInlet(info);
     public void OnSymmetryDragged(SymmetryAxisDragInfo info) => Internals.ChangeController.SymmetryDraggedInlet(info);
     public void OnSymmetryDragEnded(SymmetryAxisDirection dir) => Internals.ChangeController.SymmetryDragEndedInlet(dir);
     public void OnSymmetryDragEnded(SymmetryAxisDirection dir) => Internals.ChangeController.SymmetryDragEndedInlet(dir);

+ 9 - 3
src/PixiEditor/Models/DocumentModels/Public/DocumentToolsModule.cs

@@ -30,13 +30,19 @@ internal class DocumentToolsModule
     public void UseRectangleTool()
     public void UseRectangleTool()
     {
     {
         bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
         bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
-        Internals.ChangeController.TryStartExecutor<RectangleToolExecutor>(force);
+        Internals.ChangeController.TryStartExecutor<RasterRectangleToolExecutor>(force);
     }
     }
 
 
-    public void UseEllipseTool()
+    public void UseRasterEllipseTool()
     {
     {
         bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
         bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
-        Internals.ChangeController.TryStartExecutor<EllipseToolExecutor>(force);
+        Internals.ChangeController.TryStartExecutor<RasterEllipseToolExecutor>(force);
+    }
+    
+    public void UseVectorEllipseTool()
+    {
+        bool force = Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked;
+        Internals.ChangeController.TryStartExecutor<VectorEllipseToolExecutor>(force);
     }
     }
 
 
     public void UseLineTool()
     public void UseLineTool()

+ 0 - 37
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EllipseToolExecutor.cs

@@ -1,37 +0,0 @@
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Actions;
-using PixiEditor.ChangeableDocument.Actions.Generated;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Models.Handlers.Tools;
-using PixiEditor.Models.Tools;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
-#nullable enable
-internal class EllipseToolExecutor : ShapeToolExecutor<IEllipseToolHandler>
-{
-    private void DrawEllipseOrCircle(VecI curPos, bool firstDraw)
-    {
-        RectI rect;
-        if (firstDraw)
-            rect = new RectI(curPos, VecI.Zero);
-        else if (toolViewModel!.DrawCircle)
-            rect = GetSquaredCoordinates(startPos, curPos);
-        else
-            rect = RectI.FromTwoPixels(startPos, curPos);
-
-        lastRect = rect;
-
-        internals!.ActionAccumulator.AddActions(new DrawEllipse_Action(memberGuid, rect, 0, strokeColor, fillColor, strokeWidth, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
-    }
-
-    public override ExecutorType Type => ExecutorType.ToolLinked;
-    protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_Shear_NoPerspective;
-    protected override void DrawShape(VecI currentPos, bool firstDraw) => DrawEllipseOrCircle(currentPos, firstDraw);
-
-    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners) =>
-        new DrawEllipse_Action(memberGuid, (RectI)RectD.FromCenterAndSize(data.Center, data.Size), corners.RectRotation, strokeColor,
-            fillColor, strokeWidth, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
-
-    protected override IAction EndDrawAction() => new EndDrawEllipse_Action();
-}

+ 25 - 8
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineToolExecutor.cs

@@ -16,7 +16,7 @@ internal class LineToolExecutor : UpdateableChangeExecutor
 
 
     private VecI startPos;
     private VecI startPos;
     private Color strokeColor;
     private Color strokeColor;
-    private int strokeWidth;
+    private int StrokeWidth => toolViewModel!.ToolSize;
     private Guid memberGuid;
     private Guid memberGuid;
     private bool drawOnMask;
     private bool drawOnMask;
 
 
@@ -24,10 +24,11 @@ internal class LineToolExecutor : UpdateableChangeExecutor
     private bool started = false;
     private bool started = false;
     private bool transforming = false;
     private bool transforming = false;
     private ILineToolHandler? toolViewModel;
     private ILineToolHandler? toolViewModel;
+    private IColorsHandler? colorsVM;
 
 
     public override ExecutionState Start()
     public override ExecutionState Start()
     {
     {
-        IColorsHandler? colorsVM = GetHandler<IColorsHandler>();
+        colorsVM = GetHandler<IColorsHandler>();
         toolViewModel = GetHandler<ILineToolHandler>();
         toolViewModel = GetHandler<ILineToolHandler>();
         IStructureMemberHandler? member = document?.SelectedStructureMember;
         IStructureMemberHandler? member = document?.SelectedStructureMember;
         if (colorsVM is null || toolViewModel is null || member is null)
         if (colorsVM is null || toolViewModel is null || member is null)
@@ -41,11 +42,8 @@ internal class LineToolExecutor : UpdateableChangeExecutor
 
 
         startPos = controller!.LastPixelPosition;
         startPos = controller!.LastPixelPosition;
         strokeColor = colorsVM.PrimaryColor;
         strokeColor = colorsVM.PrimaryColor;
-        strokeWidth = toolViewModel.ToolSize;
         memberGuid = member.Id;
         memberGuid = member.Id;
-
-        colorsVM.AddSwatch(new PaletteColor(strokeColor.R, strokeColor.G, strokeColor.B));
-
+        
         return ExecutionState.Success;
         return ExecutionState.Success;
     }
     }
 
 
@@ -58,7 +56,7 @@ internal class LineToolExecutor : UpdateableChangeExecutor
         if (toolViewModel!.Snap)
         if (toolViewModel!.Snap)
             pos = ShapeToolExecutor<IShapeToolHandler>.Get45IncrementedPosition(startPos, pos);
             pos = ShapeToolExecutor<IShapeToolHandler>.Get45IncrementedPosition(startPos, pos);
         curPos = pos;
         curPos = pos;
-        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, pos, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, pos, StrokeWidth, strokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
     }
     }
 
 
     public override void OnLeftMouseButtonUp()
     public override void OnLeftMouseButtonUp()
@@ -77,7 +75,19 @@ internal class LineToolExecutor : UpdateableChangeExecutor
     {
     {
         if (!transforming)
         if (!transforming)
             return;
             return;
-        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, (VecI)start, (VecI)end, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, (VecI)start, (VecI)end, StrokeWidth, strokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        
+        startPos = (VecI)start;
+        curPos = (VecI)end;
+    }
+
+    public override void OnColorChanged(Color color, bool primary)
+    {
+        if (!primary)
+            return;
+        
+        strokeColor = color;
+        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, curPos, StrokeWidth, strokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
     }
     }
 
 
     public override void OnSelectedObjectNudged(VecI distance)
     public override void OnSelectedObjectNudged(VecI distance)
@@ -87,6 +97,11 @@ internal class LineToolExecutor : UpdateableChangeExecutor
         document!.LineToolOverlayHandler.Nudge(distance);
         document!.LineToolOverlayHandler.Nudge(distance);
     }
     }
 
 
+    public override void OnSettingsChanged(string name, object value)
+    {
+        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, curPos, StrokeWidth, strokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+    }
+
     public override void OnMidChangeUndo()
     public override void OnMidChangeUndo()
     {
     {
         if (!transforming)
         if (!transforming)
@@ -109,6 +124,8 @@ internal class LineToolExecutor : UpdateableChangeExecutor
         document!.LineToolOverlayHandler.Hide();
         document!.LineToolOverlayHandler.Hide();
         internals!.ActionAccumulator.AddFinishedActions(new EndDrawLine_Action());
         internals!.ActionAccumulator.AddFinishedActions(new EndDrawLine_Action());
         onEnded!(this);
         onEnded!(this);
+
+        colorsVM.AddSwatch(new PaletteColor(strokeColor.R, strokeColor.G, strokeColor.B));
     }
     }
 
 
     public override void ForceStop()
     public override void ForceStop()

+ 51 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs

@@ -0,0 +1,51 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.Actions.Generated;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Models.Handlers.Tools;
+using PixiEditor.Models.Tools;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+#nullable enable
+internal class RasterEllipseToolExecutor : ShapeToolExecutor<IRasterEllipseToolHandler>
+{
+    private void DrawEllipseOrCircle(VecI curPos, double rotationRad, bool firstDraw)
+    {
+        RectI rect;
+        if (firstDraw)
+            rect = new RectI(curPos, VecI.Zero);
+        else if (toolViewModel!.DrawCircle)
+            rect = GetSquaredCoordinates(startPos, curPos);
+        else
+            rect = RectI.FromTwoPixels(startPos, curPos);
+
+        lastRect = rect;
+        lastRadians = rotationRad;
+
+        internals!.ActionAccumulator.AddActions(new DrawRasterEllipse_Action(memberGuid, rect, rotationRad, StrokeColor, FillColor, StrokeWidth, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+    }
+
+    public override ExecutorType Type => ExecutorType.ToolLinked;
+    protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
+    protected override void DrawShape(VecI currentPos, double rotationRad, bool firstDraw) => DrawEllipseOrCircle(currentPos, rotationRad, firstDraw);
+    protected override IAction SettingsChangedAction()
+    {
+        return new DrawRasterEllipse_Action(memberGuid, lastRect, lastRadians, StrokeColor, FillColor, StrokeWidth, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+    }
+
+    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
+    {
+        RectI rect = (RectI)RectD.FromCenterAndSize(data.Center, data.Size);
+        double radians = corners.RectRotation;
+        
+        lastRect = rect;
+        lastRadians = radians;
+        
+        return new DrawRasterEllipse_Action(memberGuid, lastRect, lastRadians, StrokeColor,
+            FillColor, StrokeWidth, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+    }
+
+    protected override IAction EndDrawAction() => new EndDrawRasterEllipse_Action();
+}

+ 49 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs

@@ -0,0 +1,49 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.Actions.Generated;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Models.Handlers.Tools;
+using PixiEditor.Models.Tools;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+#nullable enable
+internal class RasterRectangleToolExecutor : ShapeToolExecutor<IRectangleToolHandler>
+{
+    private ShapeData lastData;
+    public override ExecutorType Type => ExecutorType.ToolLinked;
+    private void DrawRectangle(VecI curPos, double rotationRad, bool firstDraw)
+    {
+        RectI rect;
+        if (firstDraw)
+            rect = new RectI(curPos, VecI.Zero);
+        else if (toolViewModel!.DrawSquare)
+            rect = GetSquaredCoordinates(startPos, curPos);
+        else
+            rect = RectI.FromTwoPixels(startPos, curPos);
+        lastRect = rect;
+        lastRadians = rotationRad;
+        
+        lastData = new ShapeData(rect.Center, rect.Size, rotationRad, StrokeWidth, StrokeColor, FillColor);
+
+        internals!.ActionAccumulator.AddActions(new DrawRectangle_Action(memberGuid, lastData, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+    }
+
+    protected override void DrawShape(VecI currentPos, double rotationRad, bool first) => DrawRectangle(currentPos, rotationRad, first);
+    protected override IAction SettingsChangedAction()
+    {
+        lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, StrokeWidth, StrokeColor, FillColor);
+        return new DrawRectangle_Action(memberGuid, lastData, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);   
+    }
+
+    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
+    {
+        lastData = data;
+        
+        lastRadians = corners.RectRotation;
+        
+        return new DrawRectangle_Action(memberGuid, data, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+    }
+
+    protected override IAction EndDrawAction() => new EndDrawRectangle_Action();
+}

+ 0 - 33
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RectangleToolExecutor.cs

@@ -1,33 +0,0 @@
-using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Actions;
-using PixiEditor.ChangeableDocument.Actions.Generated;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Models.Handlers.Tools;
-using PixiEditor.Models.Tools;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
-#nullable enable
-internal class RectangleToolExecutor : ShapeToolExecutor<IRectangleToolHandler>
-{
-    public override ExecutorType Type => ExecutorType.ToolLinked;
-    private void DrawRectangle(VecI curPos, bool firstDraw)
-    {
-        RectI rect;
-        if (firstDraw)
-            rect = new RectI(curPos, VecI.Zero);
-        else if (toolViewModel!.DrawSquare)
-            rect = GetSquaredCoordinates(startPos, curPos);
-        else
-            rect = RectI.FromTwoPixels(startPos, curPos);
-        lastRect = rect;
-
-        internals!.ActionAccumulator.AddActions(new DrawRectangle_Action(memberGuid, new ShapeData(rect.Center, rect.Size, 0, strokeWidth, strokeColor, fillColor), drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
-    }
-
-    protected override void DrawShape(VecI currentPos, bool first) => DrawRectangle(currentPos, first);
-
-    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners) => new DrawRectangle_Action(memberGuid, data, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
-
-    protected override IAction EndDrawAction() => new EndDrawRectangle_Action();
-}

+ 35 - 16
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ShapeToolExecutor.cs

@@ -16,9 +16,9 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 
 internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T : IShapeToolHandler
 internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T : IShapeToolHandler
 {
 {
-    protected int strokeWidth;
-    protected Color fillColor;
-    protected Color strokeColor;
+    protected int StrokeWidth => toolbar.ToolSize;
+    protected Color FillColor => toolbar.Fill ? toolbar.FillColor.ToColor() : DrawingApi.Core.ColorsImpl.Colors.Transparent;
+    protected Color StrokeColor => toolbar.StrokeColor.ToColor();
     protected Guid memberGuid;
     protected Guid memberGuid;
     protected bool drawOnMask;
     protected bool drawOnMask;
 
 
@@ -26,14 +26,17 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
     protected T? toolViewModel;
     protected T? toolViewModel;
     protected VecI startPos;
     protected VecI startPos;
     protected RectI lastRect;
     protected RectI lastRect;
-
+    protected double lastRadians;
+    
     private bool noMovement = true;
     private bool noMovement = true;
+    private IBasicShapeToolbar toolbar;
+    private IColorsHandler? colorsVM;
 
 
     public override ExecutionState Start()
     public override ExecutionState Start()
     {
     {
-        IColorsHandler? colorsVM = GetHandler<IColorsHandler>();
+        colorsVM = GetHandler<IColorsHandler>();
         toolViewModel = GetHandler<T>();
         toolViewModel = GetHandler<T>();
-        IBasicShapeToolbar? toolbar = (IBasicShapeToolbar?)toolViewModel?.Toolbar;
+        toolbar = (IBasicShapeToolbar?)toolViewModel?.Toolbar;
         IStructureMemberHandler? member = document?.SelectedStructureMember;
         IStructureMemberHandler? member = document?.SelectedStructureMember;
         if (colorsVM is null || toolbar is null || member is null)
         if (colorsVM is null || toolbar is null || member is null)
             return ExecutionState.Error;
             return ExecutionState.Error;
@@ -43,18 +46,17 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
         if (!drawOnMask && member is not ILayerHandler)
         if (!drawOnMask && member is not ILayerHandler)
             return ExecutionState.Error;
             return ExecutionState.Error;
 
 
-        fillColor = toolbar.Fill ? toolbar.FillColor.ToColor() : DrawingApi.Core.ColorsImpl.Colors.Transparent;
         startPos = controller!.LastPixelPosition;
         startPos = controller!.LastPixelPosition;
-        strokeColor = colorsVM.PrimaryColor;
-        strokeWidth = toolbar.ToolSize;
         memberGuid = member.Id;
         memberGuid = member.Id;
-
-        colorsVM.AddSwatch(new PaletteColor(strokeColor.R, strokeColor.G, strokeColor.B));
-        DrawShape(startPos, true);
+        
+        OnColorChanged(colorsVM.PrimaryColor, true);
+        
+        DrawShape(startPos, 0, true);
         return ExecutionState.Success;
         return ExecutionState.Success;
     }
     }
 
 
-    protected abstract void DrawShape(VecI currentPos, bool firstDraw);
+    protected abstract void DrawShape(VecI currentPos, double rotationRad, bool firstDraw);
+    protected abstract IAction SettingsChangedAction();
     protected abstract IAction TransformMovedAction(ShapeData data, ShapeCorners corners);
     protected abstract IAction TransformMovedAction(ShapeData data, ShapeCorners corners);
     protected abstract IAction EndDrawAction();
     protected abstract IAction EndDrawAction();
     protected virtual DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
     protected virtual DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
@@ -103,8 +105,8 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
             return;
             return;
 
 
         var rect = RectD.FromCenterAndSize(corners.RectCenter, corners.RectSize);
         var rect = RectD.FromCenterAndSize(corners.RectCenter, corners.RectSize);
-        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, strokeWidth, strokeColor,
-            fillColor);
+        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, StrokeWidth, StrokeColor,
+            FillColor);
         IAction drawAction = TransformMovedAction(shapeData, corners);
         IAction drawAction = TransformMovedAction(shapeData, corners);
 
 
         internals!.ActionAccumulator.AddActions(drawAction);
         internals!.ActionAccumulator.AddActions(drawAction);
@@ -115,6 +117,18 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
         internals!.ActionAccumulator.AddFinishedActions(EndDrawAction());
         internals!.ActionAccumulator.AddFinishedActions(EndDrawAction());
         document!.TransformHandler.HideTransform();
         document!.TransformHandler.HideTransform();
         onEnded?.Invoke(this);
         onEnded?.Invoke(this);
+        
+        colorsVM.AddSwatch(StrokeColor.ToPaletteColor());
+        colorsVM.AddSwatch(FillColor.ToPaletteColor());
+    }
+    
+    public override void OnColorChanged(Color color, bool primary)
+    {
+        if (primary && toolbar.SyncWithPrimaryColor)
+        {
+            toolbar.StrokeColor = color.ToColor();
+            toolbar.FillColor = color.ToColor();
+        }
     }
     }
 
 
     public override void OnSelectedObjectNudged(VecI distance)
     public override void OnSelectedObjectNudged(VecI distance)
@@ -143,7 +157,12 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
         if (transforming)
         if (transforming)
             return;
             return;
         noMovement = false;
         noMovement = false;
-        DrawShape(pos, false);
+        DrawShape(pos, lastRadians, false);
+    }
+
+    public override void OnSettingsChanged(string name, object value)
+    {
+        internals!.ActionAccumulator.AddActions(SettingsChangedAction());
     }
     }
 
 
     public override void OnLeftMouseButtonUp()
     public override void OnLeftMouseButtonUp()

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

@@ -2,6 +2,7 @@
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Enums;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
@@ -62,4 +63,7 @@ internal abstract class UpdateableChangeExecutor
     public virtual void OnMidChangeUndo() { }
     public virtual void OnMidChangeUndo() { }
     public virtual void OnMidChangeRedo() { }
     public virtual void OnMidChangeRedo() { }
     public virtual void OnSelectedObjectNudged(VecI distance) { }
     public virtual void OnSelectedObjectNudged(VecI distance) { }
+
+    public virtual void OnSettingsChanged(string name, object value) { }
+    public virtual void OnColorChanged(Color color, bool primary) { }
 }
 }

+ 29 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs

@@ -0,0 +1,29 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.Models.Handlers.Tools;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
+
+internal class VectorEllipseToolExecutor : ShapeToolExecutor<IVectorEllipseToolHandler>
+{
+    protected override void DrawShape(VecI currentPos, double rotationRad, bool firstDraw)
+    {
+        throw new NotImplementedException();
+    }
+
+    protected override IAction SettingsChangedAction()
+    {
+        throw new NotImplementedException();
+    }
+
+    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
+    {
+        throw new NotImplementedException();
+    }
+
+    protected override IAction EndDrawAction()
+    {
+        throw new NotImplementedException();
+    }
+}

+ 4 - 2
src/PixiEditor/Models/Handlers/Toolbars/IBasicShapeToolbar.cs

@@ -4,6 +4,8 @@ namespace PixiEditor.Models.Handlers.Toolbars;
 
 
 internal interface IBasicShapeToolbar : IBasicToolbar
 internal interface IBasicShapeToolbar : IBasicToolbar
 {
 {
-    public bool Fill { get; }
-    public Color FillColor { get; }
+    public Color StrokeColor { get; set; }
+    public bool Fill { get; set; }
+    public Color FillColor { get; set; }
+    public bool SyncWithPrimaryColor { get; set; }
 }
 }

+ 4 - 1
src/PixiEditor/Models/Handlers/Toolbars/IToolbar.cs

@@ -3,10 +3,13 @@ using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 
 
 namespace PixiEditor.Models.Handlers.Toolbars;
 namespace PixiEditor.Models.Handlers.Toolbars;
 
 
+public delegate void SettingChange(string name, object value);
 internal interface IToolbar : IHandler
 internal interface IToolbar : IHandler
 {
 {
+    public void AddSetting(Setting setting);
     public Setting GetSetting(string name);
     public Setting GetSetting(string name);
-    public ObservableCollection<Setting> Settings { get; set; }
+    public IReadOnlyList<Setting> Settings { get; }
     public void SaveToolbarSettings();
     public void SaveToolbarSettings();
     public void LoadSharedSettings();
     public void LoadSharedSettings();
+    public event SettingChange SettingChanged;
 }
 }

+ 1 - 1
src/PixiEditor/Models/Handlers/Tools/IEllipseToolHandler.cs → src/PixiEditor/Models/Handlers/Tools/IRasterEllipseToolHandler.cs

@@ -1,6 +1,6 @@
 namespace PixiEditor.Models.Handlers.Tools;
 namespace PixiEditor.Models.Handlers.Tools;
 
 
-internal interface IEllipseToolHandler : IShapeToolHandler
+internal interface IRasterEllipseToolHandler : IShapeToolHandler
 {
 {
     public bool DrawCircle { get; }
     public bool DrawCircle { get; }
 }
 }

+ 6 - 0
src/PixiEditor/Models/Handlers/Tools/IVectorEllipseToolHandler.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Models.Handlers.Tools;
+
+internal interface IVectorEllipseToolHandler : IShapeToolHandler
+{
+    
+}

+ 1 - 1
src/PixiEditor/PixiEditor.csproj

@@ -101,7 +101,7 @@
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
     <PackageReference Include="PixiEditor.ColorPicker.Models" Version="1.0.5"/>
     <PackageReference Include="PixiEditor.ColorPicker.Models" Version="1.0.5"/>
-    <PackageReference Include="PixiEditor.ColorPicker.AvaloniaUI" Version="1.0.5"/>
+    <PackageReference Include="PixiEditor.ColorPicker.AvaloniaUI" Version="1.0.6"/>
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 91 - 34
src/PixiEditor/ViewModels/SubViewModels/ColorsViewModel.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Avalonia;
 using Avalonia;
@@ -115,13 +116,16 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         doc.Operations.ReplaceColor(colors.oldColor, colors.newColor, doc.AnimationDataViewModel.ActiveFrameBindable);
         doc.Operations.ReplaceColor(colors.oldColor, colors.newColor, doc.AnimationDataViewModel.ActiveFrameBindable);
     }
     }
 
 
-    [Commands_Command.Basic("PixiEditor.Colors.ReplaceSecondaryByPrimaryColor", false, "REPLACE_SECONDARY_BY_PRIMARY", "REPLACE_SECONDARY_BY_PRIMARY", IconEvaluator = "PixiEditor.Colors.ReplaceColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.ReplacePrimaryBySecondaryColor", true, "REPLACE_PRIMARY_BY_SECONDARY", "REPLACE_PRIMARY_BY_SECONDARY_DESCRIPTIVE", IconEvaluator = "PixiEditor.Colors.ReplaceColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.ReplaceSecondaryByPrimaryColor", false, "REPLACE_SECONDARY_BY_PRIMARY",
+        "REPLACE_SECONDARY_BY_PRIMARY", IconEvaluator = "PixiEditor.Colors.ReplaceColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.ReplacePrimaryBySecondaryColor", true, "REPLACE_PRIMARY_BY_SECONDARY",
+        "REPLACE_PRIMARY_BY_SECONDARY_DESCRIPTIVE", IconEvaluator = "PixiEditor.Colors.ReplaceColorIcon",
+        AnalyticsTrack = true)]
     public void ReplaceColors(bool replacePrimary)
     public void ReplaceColors(bool replacePrimary)
     {
     {
         PaletteColor oldColor = replacePrimary ? PrimaryColor.ToPaletteColor() : SecondaryColor.ToPaletteColor();
         PaletteColor oldColor = replacePrimary ? PrimaryColor.ToPaletteColor() : SecondaryColor.ToPaletteColor();
         PaletteColor newColor = replacePrimary ? SecondaryColor.ToPaletteColor() : PrimaryColor.ToPaletteColor();
         PaletteColor newColor = replacePrimary ? SecondaryColor.ToPaletteColor() : PrimaryColor.ToPaletteColor();
-        
+
         ReplaceColors((oldColor, newColor));
         ReplaceColors((oldColor, newColor));
     }
     }
 
 
@@ -134,28 +138,27 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
             Models.Commands.Commands.Command cmd => (bool)cmd.GetParameter(),
             Models.Commands.Commands.Command cmd => (bool)cmd.GetParameter(),
             _ => false
             _ => false
         };
         };
-        
+
         var oldColor = replacePrimary ? PrimaryColor : SecondaryColor;
         var oldColor = replacePrimary ? PrimaryColor : SecondaryColor;
         var newColor = replacePrimary ? SecondaryColor : PrimaryColor;
         var newColor = replacePrimary ? SecondaryColor : PrimaryColor;
-        
-        var oldDrawing = new GeometryDrawing { Brush = new SolidColorBrush(oldColor.ToOpaqueMediaColor()), Pen = new Pen(Brushes.Gray, .5) };
+
+        var oldDrawing = new GeometryDrawing
+        {
+            Brush = new SolidColorBrush(oldColor.ToOpaqueMediaColor()), Pen = new Pen(Brushes.Gray, .5)
+        };
         var oldGeometry = new EllipseGeometry(new Rect(5, 5, 5, 5));
         var oldGeometry = new EllipseGeometry(new Rect(5, 5, 5, 5));
-        
+
         oldDrawing.Geometry = oldGeometry;
         oldDrawing.Geometry = oldGeometry;
-        
-        var newDrawing = new GeometryDrawing { Brush = new SolidColorBrush(newColor.ToOpaqueMediaColor()), Pen = new Pen(Brushes.White, 1) };
+
+        var newDrawing = new GeometryDrawing
+        {
+            Brush = new SolidColorBrush(newColor.ToOpaqueMediaColor()), Pen = new Pen(Brushes.White, 1)
+        };
         var newGeometry = new EllipseGeometry(new Rect(10, 10, 6, 6));
         var newGeometry = new EllipseGeometry(new Rect(10, 10, 6, 6));
 
 
         newDrawing.Geometry = newGeometry;
         newDrawing.Geometry = newGeometry;
-        
-        return new DrawingImage(new DrawingGroup
-        {
-            Children = new DrawingCollection
-            {
-                oldDrawing,
-                newDrawing
-            }
-        });
+
+        return new DrawingImage(new DrawingGroup { Children = new DrawingCollection { oldDrawing, newDrawing } });
     }
     }
 
 
     private async void OwnerOnStartupEvent(object sender, EventArgs e)
     private async void OwnerOnStartupEvent(object sender, EventArgs e)
@@ -163,14 +166,16 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         await ImportLospecPalette();
         await ImportLospecPalette();
     }
     }
 
 
-    [Commands_Command.Basic("PixiEditor.Colors.OpenPaletteBrowser", "OPEN_PALETTE_BROWSER", "OPEN_PALETTE_BROWSER", CanExecute = "PixiEditor.HasDocument", 
-        Icon = PixiPerfectIcons.Database, MenuItemPath = "VIEW/OPEN_PALETTE_BROWSER", MenuItemOrder = 3, AnalyticsTrack = true)]
-    public void OpenPalettesBrowser() 
+    [Commands_Command.Basic("PixiEditor.Colors.OpenPaletteBrowser", "OPEN_PALETTE_BROWSER", "OPEN_PALETTE_BROWSER",
+        CanExecute = "PixiEditor.HasDocument",
+        Icon = PixiPerfectIcons.Database, MenuItemPath = "VIEW/OPEN_PALETTE_BROWSER", MenuItemOrder = 3,
+        AnalyticsTrack = true)]
+    public void OpenPalettesBrowser()
     {
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         if (doc is not null)
         if (doc is not null)
             PalettesBrowser.Open();
             PalettesBrowser.Open();
-    } 
+    }
 
 
     private async Task ImportLospecPalette()
     private async Task ImportLospecPalette()
     {
     {
@@ -238,7 +243,8 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         return paletteColors is not null && Owner.DocumentIsNotNull(paletteColors) && paletteColors.Count > 0;
         return paletteColors is not null && Owner.DocumentIsNotNull(paletteColors) && paletteColors.Count > 0;
     }
     }
 
 
-    [Commands_Command.Internal("PixiEditor.Colors.ImportPalette", CanExecute = "PixiEditor.Colors.CanImportPalette", AnalyticsTrack = true)]
+    [Commands_Command.Internal("PixiEditor.Colors.ImportPalette", CanExecute = "PixiEditor.Colors.CanImportPalette",
+        AnalyticsTrack = true)]
     public async Task ImportPalette(List<PaletteColor> palette)
     public async Task ImportPalette(List<PaletteColor> palette)
     {
     {
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -262,22 +268,31 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
 
 
     [Evaluator.Icon("PixiEditor.Colors.FirstPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.FirstPaletteColorIcon")]
     public IImage GetPaletteColorIcon1() => GetPaletteColorIcon(0);
     public IImage GetPaletteColorIcon1() => GetPaletteColorIcon(0);
+
     [Evaluator.Icon("PixiEditor.Colors.SecondPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.SecondPaletteColorIcon")]
     public IImage GetPaletteColorIcon2() => GetPaletteColorIcon(1);
     public IImage GetPaletteColorIcon2() => GetPaletteColorIcon(1);
+
     [Evaluator.Icon("PixiEditor.Colors.ThirdPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.ThirdPaletteColorIcon")]
     public IImage GetPaletteColorIcon3() => GetPaletteColorIcon(2);
     public IImage GetPaletteColorIcon3() => GetPaletteColorIcon(2);
+
     [Evaluator.Icon("PixiEditor.Colors.FourthPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.FourthPaletteColorIcon")]
     public IImage GetPaletteColorIcon4() => GetPaletteColorIcon(3);
     public IImage GetPaletteColorIcon4() => GetPaletteColorIcon(3);
+
     [Evaluator.Icon("PixiEditor.Colors.FifthPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.FifthPaletteColorIcon")]
     public IImage GetPaletteColorIcon5() => GetPaletteColorIcon(4);
     public IImage GetPaletteColorIcon5() => GetPaletteColorIcon(4);
+
     [Evaluator.Icon("PixiEditor.Colors.SixthPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.SixthPaletteColorIcon")]
     public IImage GetPaletteColorIcon6() => GetPaletteColorIcon(5);
     public IImage GetPaletteColorIcon6() => GetPaletteColorIcon(5);
+
     [Evaluator.Icon("PixiEditor.Colors.SeventhPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.SeventhPaletteColorIcon")]
     public IImage GetPaletteColorIcon7() => GetPaletteColorIcon(6);
     public IImage GetPaletteColorIcon7() => GetPaletteColorIcon(6);
+
     [Evaluator.Icon("PixiEditor.Colors.EighthPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.EighthPaletteColorIcon")]
     public IImage GetPaletteColorIcon8() => GetPaletteColorIcon(7);
     public IImage GetPaletteColorIcon8() => GetPaletteColorIcon(7);
+
     [Evaluator.Icon("PixiEditor.Colors.NinthPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.NinthPaletteColorIcon")]
     public IImage GetPaletteColorIcon9() => GetPaletteColorIcon(8);
     public IImage GetPaletteColorIcon9() => GetPaletteColorIcon(8);
+
     [Evaluator.Icon("PixiEditor.Colors.TenthPaletteColorIcon")]
     [Evaluator.Icon("PixiEditor.Colors.TenthPaletteColorIcon")]
     public IImage GetPaletteColorIcon10() => GetPaletteColorIcon(9);
     public IImage GetPaletteColorIcon10() => GetPaletteColorIcon(9);
 
 
@@ -300,16 +315,41 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         return ColorSearchResult.GetIcon(color);
         return ColorSearchResult.GetIcon(color);
     }
     }
 
 
-    [Commands_Command.Basic("PixiEditor.Colors.SelectFirstPaletteColor", "SELECT_COLOR_1", "SELECT_COLOR_1_DESCRIPTIVE", Key = Key.D1, Parameter = 0, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.FirstPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectSecondPaletteColor", "SELECT_COLOR_2", "SELECT_COLOR_2_DESCRIPTIVE", Key = Key.D2, Parameter = 1, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.SecondPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectThirdPaletteColor", "SELECT_COLOR_3", "SELECT_COLOR_3_DESCRIPTIVE", Key = Key.D3, Parameter = 2, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.ThirdPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectFourthPaletteColor", "SELECT_COLOR_4", "SELECT_COLOR_4_DESCRIPTIVE", Key = Key.D4, Parameter = 3, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.FourthPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectFifthPaletteColor", "SELECT_COLOR_5", "SELECT_COLOR_5_DESCRIPTIVE", Key = Key.D5, Parameter = 4, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.FifthPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectSixthPaletteColor", "SELECT_COLOR_6", "SELECT_COLOR_6_DESCRIPTIVE", Key = Key.D6, Parameter = 5, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.SixthPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectSeventhPaletteColor", "SELECT_COLOR_7", "SELECT_COLOR_7_DESCRIPTIVE", Key = Key.D7, Parameter = 6, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.SeventhPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectEighthPaletteColor", "SELECT_COLOR_8", "SELECT_COLOR_8_DESCRIPTIVE", Key = Key.D8, Parameter = 7, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.EighthPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectNinthPaletteColor", "SELECT_COLOR_9", "SELECT_COLOR_9_DESCRIPTIVE", Key = Key.D9, Parameter = 8, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.NinthPaletteColorIcon", AnalyticsTrack = true)]
-    [Commands_Command.Basic("PixiEditor.Colors.SelectTenthPaletteColor", "SELECT_COLOR_10", "SELECT_COLOR_10_DESCRIPTIVE", Key = Key.D0, Parameter = 9, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor", IconEvaluator = "PixiEditor.Colors.TenthPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectFirstPaletteColor", "SELECT_COLOR_1", "SELECT_COLOR_1_DESCRIPTIVE",
+        Key = Key.D1, Parameter = 0, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.FirstPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectSecondPaletteColor", "SELECT_COLOR_2",
+        "SELECT_COLOR_2_DESCRIPTIVE", Key = Key.D2, Parameter = 1,
+        CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.SecondPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectThirdPaletteColor", "SELECT_COLOR_3", "SELECT_COLOR_3_DESCRIPTIVE",
+        Key = Key.D3, Parameter = 2, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.ThirdPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectFourthPaletteColor", "SELECT_COLOR_4",
+        "SELECT_COLOR_4_DESCRIPTIVE", Key = Key.D4, Parameter = 3,
+        CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.FourthPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectFifthPaletteColor", "SELECT_COLOR_5", "SELECT_COLOR_5_DESCRIPTIVE",
+        Key = Key.D5, Parameter = 4, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.FifthPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectSixthPaletteColor", "SELECT_COLOR_6", "SELECT_COLOR_6_DESCRIPTIVE",
+        Key = Key.D6, Parameter = 5, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.SixthPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectSeventhPaletteColor", "SELECT_COLOR_7",
+        "SELECT_COLOR_7_DESCRIPTIVE", Key = Key.D7, Parameter = 6,
+        CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.SeventhPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectEighthPaletteColor", "SELECT_COLOR_8",
+        "SELECT_COLOR_8_DESCRIPTIVE", Key = Key.D8, Parameter = 7,
+        CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.EighthPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectNinthPaletteColor", "SELECT_COLOR_9", "SELECT_COLOR_9_DESCRIPTIVE",
+        Key = Key.D9, Parameter = 8, CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.NinthPaletteColorIcon", AnalyticsTrack = true)]
+    [Commands_Command.Basic("PixiEditor.Colors.SelectTenthPaletteColor", "SELECT_COLOR_10",
+        "SELECT_COLOR_10_DESCRIPTIVE", Key = Key.D0, Parameter = 9,
+        CanExecute = "PixiEditor.Colors.CanSelectPaletteColor",
+        IconEvaluator = "PixiEditor.Colors.TenthPaletteColorIcon", AnalyticsTrack = true)]
     public void SelectPaletteColor(int index)
     public void SelectPaletteColor(int index)
     {
     {
         var document = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var document = Owner.DocumentManagerSubViewModel.ActiveDocument;
@@ -356,7 +396,8 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
         PrimaryColor = color.ToColor();
         PrimaryColor = color.ToColor();
     }
     }
 
 
-    [Commands_Command.Basic("PixIEditor.Colors.AddPrimaryToPalettes", "ADD_PRIMARY_COLOR_TO_PALETTE", "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE", CanExecute = "PixiEditor.HasDocument", 
+    [Commands_Command.Basic("PixIEditor.Colors.AddPrimaryToPalettes", "ADD_PRIMARY_COLOR_TO_PALETTE",
+        "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE", CanExecute = "PixiEditor.HasDocument",
         Icon = PixiPerfectIcons.CopyAdd, AnalyticsTrack = true)]
         Icon = PixiPerfectIcons.CopyAdd, AnalyticsTrack = true)]
     public void AddPrimaryColorToPalette()
     public void AddPrimaryColorToPalette()
     {
     {
@@ -386,4 +427,20 @@ internal class ColorsViewModel : SubViewModel<ViewModelMain>, IColorsHandler
             PaletteProvider.RegisterDataSource(dataSource);
             PaletteProvider.RegisterDataSource(dataSource);
         }
         }
     }
     }
+
+    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
+    {
+        base.OnPropertyChanged(e);
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (doc == null) return;
+
+        if (e.PropertyName == nameof(PrimaryColor))
+        {
+            doc.EventInlet.PrimaryColorChanged(PrimaryColor);
+        }
+        else if (e.PropertyName == nameof(SecondaryColor))
+        {
+            doc.EventInlet.SecondaryColorChanged(SecondaryColor);
+        }
+    }
 }
 }

+ 20 - 1
src/PixiEditor/ViewModels/SubViewModels/ToolsViewModel.cs

@@ -206,7 +206,11 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
             return;
             return;
         }
         }
 
 
-        ActiveTool?.OnDeselecting();
+        if (ActiveTool != null)
+        {
+            ActiveTool.OnDeselecting();
+            ActiveTool.Toolbar.SettingChanged -= ToolbarSettingChanged;
+        }
 
 
         if (ActiveTool != null) ActiveTool.IsTransient = false;
         if (ActiveTool != null) ActiveTool.IsTransient = false;
         bool shareToolbar = EnableSharedToolbar;
         bool shareToolbar = EnableSharedToolbar;
@@ -219,6 +223,8 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
 
 
         LastActionTool = ActiveTool;
         LastActionTool = ActiveTool;
         ActiveTool = tool;
         ActiveTool = tool;
+        
+        ActiveTool.Toolbar.SettingChanged += ToolbarSettingChanged;
 
 
         if (shareToolbar)
         if (shareToolbar)
         {
         {
@@ -362,6 +368,15 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
         ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
         ActiveTool?.ModifierKeyChanged(args.IsCtrlDown, args.IsShiftDown, args.IsAltDown);
     }
     }
     
     
+    private void ToolbarSettingChanged(string settingName, object value)
+    {
+        var document = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        if (document is null)
+            return;
+        
+        document.EventInlet.SettingsChanged(settingName, value);
+    }
+    
     private void AddToolSets(ToolSetsConfig toolSetConfig)
     private void AddToolSets(ToolSetsConfig toolSetConfig)
     {
     {
         foreach (ToolSetConfig toolSet in toolSetConfig)
         foreach (ToolSetConfig toolSet in toolSetConfig)
@@ -373,7 +388,11 @@ internal class ToolsViewModel : SubViewModel<ViewModelMain>, IToolsHandler
                 IToolHandler? tool = allTools.FirstOrDefault(tool => tool.ToolName == toolName);
                 IToolHandler? tool = allTools.FirstOrDefault(tool => tool.ToolName == toolName);
                 if (tool is null)
                 if (tool is null)
                 {
                 {
+                    #if DEBUG
                     throw new InvalidOperationException($"Tool '{toolName}' not found.");
                     throw new InvalidOperationException($"Tool '{toolName}' not found.");
+                    #endif
+                    
+                    continue;
                 }
                 }
                 
                 
                 tools.Add(tool);
                 tools.Add(tool);

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

@@ -6,12 +6,59 @@ namespace PixiEditor.ViewModels.Tools.ToolSettings.Toolbars;
 #nullable enable
 #nullable enable
 internal class BasicShapeToolbar : BasicToolbar, IBasicShapeToolbar
 internal class BasicShapeToolbar : BasicToolbar, IBasicShapeToolbar
 {
 {
-    public bool Fill => GetSetting<BoolSettingViewModel>(nameof(Fill)).Value;
-    public Color FillColor => GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value;
+    public Color StrokeColor
+    {
+        get
+        {
+            return GetSetting<ColorSettingViewModel>(nameof(StrokeColor)).Value;
+        }
+        set
+        {
+            GetSetting<ColorSettingViewModel>(nameof(StrokeColor)).Value = value;
+        }
+    }
+
+    public bool Fill
+    {
+        get
+        {
+            return GetSetting<BoolSettingViewModel>(nameof(Fill)).Value;
+        }
+        set
+        {
+            GetSetting<BoolSettingViewModel>(nameof(Fill)).Value = value;
+        }
+    }
+
+    public Color FillColor
+    {
+        get
+        {
+            return GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value;
+        }
+        set
+        {
+            GetSetting<ColorSettingViewModel>(nameof(FillColor)).Value = value;
+        }
+    }
+
+    public bool SyncWithPrimaryColor
+    {
+        get
+        {
+            return GetSetting<BoolSettingViewModel>(nameof(SyncWithPrimaryColor)).Value;
+        }
+        set
+        {
+            GetSetting<BoolSettingViewModel>(nameof(SyncWithPrimaryColor)).Value = value;
+        }
+    } 
 
 
     public BasicShapeToolbar()
     public BasicShapeToolbar()
     {
     {
-        Settings.Add(new BoolSettingViewModel(nameof(Fill), "FILL_SHAPE_LABEL"));
-        Settings.Add(new ColorSettingViewModel(nameof(FillColor), "FILL_COLOR_LABEL"));
+        AddSetting(new ColorSettingViewModel(nameof(StrokeColor), "STROKE_COLOR_LABEL"));
+        AddSetting(new BoolSettingViewModel(nameof(Fill), "FILL_SHAPE_LABEL") { Value = true });
+        AddSetting(new ColorSettingViewModel(nameof(FillColor), "FILL_COLOR_LABEL"));
+        AddSetting(new BoolSettingViewModel(nameof(SyncWithPrimaryColor), "SYNC_WITH_PRIMARY_COLOR_LABEL") { Value = true });
     }
     }
 }
 }

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

@@ -17,7 +17,7 @@ internal class BasicToolbar : Toolbar, IBasicToolbar
     {
     {
         var setting = new SizeSettingViewModel(nameof(ToolSize), "TOOL_SIZE_LABEL");
         var setting = new SizeSettingViewModel(nameof(ToolSize), "TOOL_SIZE_LABEL");
         setting.ValueChanged += (_, _) => OnPropertyChanged(nameof(ToolSize));
         setting.ValueChanged += (_, _) => OnPropertyChanged(nameof(ToolSize));
-        Settings.Add(setting);
+        AddSetting(setting);
     }
     }
 
 
     public override void OnLoadedSettings()
     public override void OnLoadedSettings()

+ 14 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/Toolbar.cs

@@ -11,7 +11,18 @@ internal abstract class Toolbar : ObservableObject, IToolbar
 {
 {
     private static readonly List<Setting> SharedSettings = new List<Setting>();
     private static readonly List<Setting> SharedSettings = new List<Setting>();
 
 
-    public ObservableCollection<Setting> Settings { get; set; } = new ObservableCollection<Setting>();
+    private ObservableCollection<Setting> settings = new();
+    public IReadOnlyList<Setting> Settings => settings; 
+
+    public void AddSetting(Setting setting)
+    {
+        setting.ValueChanged += (sender, args) =>
+        {
+            SettingChanged?.Invoke(setting.Name, setting.Value);
+        };
+        
+        settings.Add(setting);
+    }
 
 
     /// <summary>
     /// <summary>
     ///     Gets setting in toolbar by name.
     ///     Gets setting in toolbar by name.
@@ -75,6 +86,8 @@ internal abstract class Toolbar : ObservableObject, IToolbar
         OnLoadedSettings();
         OnLoadedSettings();
     }
     }
 
 
+    public event SettingChange? SettingChanged;
+
     public virtual void OnLoadedSettings()
     public virtual void OnLoadedSettings()
     {
     {
     }
     }

+ 1 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/ToolbarFactory.cs

@@ -30,7 +30,7 @@ internal static class ToolbarFactory
             {
             {
                 var setting = CreateSetting(property.PropertyType, name, attribute, label);
                 var setting = CreateSetting(property.PropertyType, name, attribute, label);
                 AddValueChangedHandlerIfRequired(toolType, tool, setting, attribute);
                 AddValueChangedHandlerIfRequired(toolType, tool, setting, attribute);
-                toolbar.Settings.Add(setting);
+                toolbar.AddSetting(setting);
             }
             }
         }
         }
 
 

+ 3 - 3
src/PixiEditor/ViewModels/Tools/Tools/EllipseToolViewModel.cs → src/PixiEditor/ViewModels/Tools/Tools/RasterEllipseToolViewModel.cs

@@ -9,12 +9,12 @@ using PixiEditor.UI.Common.Fonts;
 namespace PixiEditor.ViewModels.Tools.Tools;
 namespace PixiEditor.ViewModels.Tools.Tools;
 
 
 [Command.Tool(Key = Key.C)]
 [Command.Tool(Key = Key.C)]
-internal class EllipseToolViewModel : ShapeTool, IEllipseToolHandler
+internal class RasterEllipseToolViewModel : ShapeTool, IRasterEllipseToolHandler
 {
 {
     private string defaultActionDisplay = "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT";
     private string defaultActionDisplay = "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT";
     public override string ToolNameLocalizationKey => "ELLIPSE_TOOL";
     public override string ToolNameLocalizationKey => "ELLIPSE_TOOL";
 
 
-    public EllipseToolViewModel()
+    public RasterEllipseToolViewModel()
     {
     {
         ActionDisplay = defaultActionDisplay;
         ActionDisplay = defaultActionDisplay;
     }
     }
@@ -40,6 +40,6 @@ internal class EllipseToolViewModel : ShapeTool, IEllipseToolHandler
 
 
     public override void UseTool(VecD pos)
     public override void UseTool(VecD pos)
     {
     {
-        ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseEllipseTool();
+        ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseRasterEllipseTool();
     }
     }
 }
 }

+ 27 - 0
src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs

@@ -0,0 +1,27 @@
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Models.Handlers.Tools;
+using PixiEditor.Numerics;
+using PixiEditor.UI.Common.Fonts;
+
+namespace PixiEditor.ViewModels.Tools.Tools;
+
+internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
+{
+    private string defaultActionDisplay = "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT";
+    public override string ToolNameLocalizationKey => "ELLIPSE_TOOL";
+
+    public VectorEllipseToolViewModel()
+    {
+        ActionDisplay = defaultActionDisplay;
+    }
+
+    public override LocalizedString Tooltip => new LocalizedString("ELLIPSE_TOOL_TOOLTIP", Shortcut);
+    public bool DrawCircle { get; private set; }
+
+    public override string Icon => PixiPerfectIcons.Circle;
+
+    public override void UseTool(VecD pos)
+    {
+        ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseRasterEllipseTool();
+    }
+}

+ 2 - 0
src/PixiEditor/Views/Input/ToolSettingColorPicker.axaml.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Media;
 using Avalonia.Media;
+using Avalonia.Threading;
 
 
 namespace PixiEditor.Views.Input;
 namespace PixiEditor.Views.Input;
 
 
@@ -14,6 +15,7 @@ internal partial class ToolSettingColorPicker : UserControl
         get => GetValue(SelectedColorProperty);
         get => GetValue(SelectedColorProperty);
         set => SetValue(SelectedColorProperty, value);
         set => SetValue(SelectedColorProperty, value);
     }
     }
+    
     public ToolSettingColorPicker()
     public ToolSettingColorPicker()
     {
     {
         InitializeComponent();
         InitializeComponent();