Browse Source

Merge pull request #870 from PixiEditor/fixes/28.03.2025

Fixes/28.03.2025
Krzysztof Krysiński 4 months ago
parent
commit
71f3b3924e
27 changed files with 617 additions and 107 deletions
  1. 1 1
      src/Drawie
  2. 30 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs
  3. 29 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/LineVectorData.cs
  4. 30 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PathVectorData.cs
  5. 30 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsVectorData.cs
  6. 30 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs
  7. 26 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/ShapeVectorData.cs
  8. 58 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/TextVectorData.cs
  9. 43 4
      src/PixiEditor.ChangeableDocument/Changes/Vectors/SetShapeGeometry_UpdateableChange.cs
  10. 38 16
      src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs
  11. 1 1
      src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs
  12. 65 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs
  13. 1 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IExecutorFeature.cs
  14. 4 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs
  15. 4 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs
  16. 1 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs
  17. 1 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterLineToolExecutor.cs
  18. 1 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs
  19. 8 4
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs
  20. 2 2
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs
  21. 6 3
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs
  22. 26 7
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs
  23. 53 11
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorLineToolExecutor.cs
  24. 34 21
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs
  25. 29 7
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorRectangleToolExecutor.cs
  26. 65 12
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs
  27. 1 1
      src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 4037b56fc0accf094d10fd445f7ff93ed23131f5
+Subproject commit 02c8bda10d6536f1d8a613ef877526756a789132

+ 30 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs

@@ -98,4 +98,34 @@ public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
 
 
         return path;
         return path;
     }
     }
+
+    protected bool Equals(EllipseVectorData other)
+    {
+        return base.Equals(other) && Radius.Equals(other.Radius) && Center.Equals(other.Center);
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((EllipseVectorData)obj);
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(base.GetHashCode(), Radius, Center);
+    }
 }
 }

+ 29 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/LineVectorData.cs

@@ -121,4 +121,33 @@ public class LineVectorData : ShapeVectorData, IReadOnlyLineData
         return path;
         return path;
     }
     }
 
 
+    protected bool Equals(LineVectorData other)
+    {
+        return base.Equals(other) && Start.Equals(other.Start) && End.Equals(other.End);
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((LineVectorData)obj);
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(base.GetHashCode(), Start, End);
+    }
 }
 }

+ 30 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PathVectorData.cs

@@ -115,4 +115,34 @@ public class PathVectorData : ShapeVectorData, IReadOnlyPathData
 
 
         return newPath;
         return newPath;
     }
     }
+
+    protected bool Equals(PathVectorData other)
+    {
+        return base.Equals(other) && Path.Equals(other.Path) && StrokeLineCap == other.StrokeLineCap && StrokeLineJoin == other.StrokeLineJoin;
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((PathVectorData)obj);
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(base.GetHashCode(), Path, (int)StrokeLineCap, (int)StrokeLineJoin);
+    }
 }
 }

+ 30 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsVectorData.cs

@@ -92,4 +92,34 @@ public class PointsVectorData : ShapeVectorData
 
 
         return path;
         return path;
     }
     }
+
+    protected bool Equals(PointsVectorData other)
+    {
+        return base.Equals(other) && (Points.Equals(other.Points) || Points.SequenceEqual(other.Points));
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((PointsVectorData)obj);
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(base.GetHashCode(), Points);
+    }
 }
 }

+ 30 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs

@@ -106,4 +106,34 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
 
 
         return path;
         return path;
     }
     }
+
+    protected bool Equals(RectangleVectorData other)
+    {
+        return base.Equals(other) && Center.Equals(other.Center) && Size.Equals(other.Size);
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((RectangleVectorData)obj);
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(base.GetHashCode(), Center, Size);
+    }
 }
 }

+ 26 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/ShapeVectorData.cs

@@ -15,7 +15,6 @@ public abstract class ShapeVectorData : ICacheable, ICloneable, IReadOnlyShapeVe
     private float strokeWidth = 0;
     private float strokeWidth = 0;
 
 
     public Matrix3X3 TransformationMatrix { get; set; } = Matrix3X3.Identity;
     public Matrix3X3 TransformationMatrix { get; set; } = Matrix3X3.Identity;
-
     public Paintable Stroke { get; set; } = Colors.White;
     public Paintable Stroke { get; set; } = Colors.White;
     public Paintable FillPaintable { get; set; } = Colors.White;
     public Paintable FillPaintable { get; set; } = Colors.White;
 
 
@@ -78,8 +77,33 @@ public abstract class ShapeVectorData : ICacheable, ICloneable, IReadOnlyShapeVe
 
 
     public override int GetHashCode()
     public override int GetHashCode()
     {
     {
-        return GetCacheHash();
+        return HashCode.Combine(TransformationMatrix, Stroke, FillPaintable, Fill);
     }
     }
 
 
     public abstract VectorPath ToPath(bool transformed = false);
     public abstract VectorPath ToPath(bool transformed = false);
+
+    protected bool Equals(ShapeVectorData other)
+    {
+        return TransformationMatrix.Equals(other.TransformationMatrix) && Stroke.Equals(other.Stroke) && FillPaintable.Equals(other.FillPaintable) && Fill == other.Fill && StrokeWidth.Equals(other.StrokeWidth);
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((ShapeVectorData)obj);
+    }
 }
 }

+ 58 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/TextVectorData.cs

@@ -12,6 +12,8 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 
 
 public class TextVectorData : ShapeVectorData, IReadOnlyTextData, IScalable
 public class TextVectorData : ShapeVectorData, IReadOnlyTextData, IScalable
 {
 {
+    private bool bold;
+    private bool italic;
     private string text;
     private string text;
     private Font font = Font.CreateDefault();
     private Font font = Font.CreateDefault();
     private double? spacing = null;
     private double? spacing = null;
@@ -55,6 +57,28 @@ public class TextVectorData : ShapeVectorData, IReadOnlyTextData, IScalable
         }
         }
     }
     }
 
 
+    public bool Bold
+    {
+        get => bold;
+        set
+        {
+            bold = value;
+            Font.Bold = value;
+            lastBounds = richText.MeasureBounds(Font);
+        }
+    }
+
+    public bool Italic
+    {
+        get => italic;
+        set
+        {
+            italic = value;
+            Font.Italic = value;
+            lastBounds = richText.MeasureBounds(Font);
+        }
+    }
+
     private void FontChanged()
     private void FontChanged()
     {
     {
         if (richText == null)
         if (richText == null)
@@ -235,6 +259,9 @@ public class TextVectorData : ShapeVectorData, IReadOnlyTextData, IScalable
         hash.Add(AntiAlias);
         hash.Add(AntiAlias);
         hash.Add(MissingFontFamily);
         hash.Add(MissingFontFamily);
         hash.Add(MissingFontText);
         hash.Add(MissingFontText);
+        hash.Add(MaxWidth);
+        hash.Add(Bold);
+        hash.Add(Italic);
 
 
         return hash.ToHashCode();
         return hash.ToHashCode();
     }
     }
@@ -257,4 +284,35 @@ public class TextVectorData : ShapeVectorData, IReadOnlyTextData, IScalable
 
 
         lastBounds = richText.MeasureBounds(Font);
         lastBounds = richText.MeasureBounds(Font);
     }
     }
+
+    protected bool Equals(TextVectorData other)
+    {
+        return base.Equals(other) && Position.Equals(other.Position) && MaxWidth.Equals(other.MaxWidth) && AntiAlias == other.AntiAlias && Nullable.Equals(MissingFontFamily, other.MissingFontFamily) && MissingFontText == other.MissingFontText
+            && Text == other.Text && Font.Equals(other.Font) && Spacing.Equals(other.Spacing) && Path == other.Path && Bold == other.Bold && Italic == other.Italic;
+    }
+
+    public override bool Equals(object? obj)
+    {
+        if (obj is null)
+        {
+            return false;
+        }
+
+        if (ReferenceEquals(this, obj))
+        {
+            return true;
+        }
+
+        if (obj.GetType() != GetType())
+        {
+            return false;
+        }
+
+        return Equals((TextVectorData)obj);
+    }
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(base.GetHashCode(), Position, MaxWidth, AntiAlias, MissingFontFamily, MissingFontText, Font, HashCode.Combine(Text, Spacing, Path));
+    }
 }
 }

+ 43 - 4
src/PixiEditor.ChangeableDocument/Changes/Vectors/SetShapeGeometry_UpdateableChange.cs

@@ -10,23 +10,29 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
 {
 {
     public Guid TargetId { get; set; }
     public Guid TargetId { get; set; }
     public ShapeVectorData Data { get; set; }
     public ShapeVectorData Data { get; set; }
+    public VectorShapeChangeType ChangeType { get; set; }
 
 
     private ShapeVectorData? originalData;
     private ShapeVectorData? originalData;
 
 
     private AffectedArea lastAffectedArea;
     private AffectedArea lastAffectedArea;
 
 
     [GenerateUpdateableChangeActions]
     [GenerateUpdateableChangeActions]
-    public SetShapeGeometry_UpdateableChange(Guid targetId, ShapeVectorData data)
+    public SetShapeGeometry_UpdateableChange(Guid targetId, ShapeVectorData data, VectorShapeChangeType changeType)
     {
     {
         TargetId = targetId;
         TargetId = targetId;
         Data = data;
         Data = data;
+        ChangeType = changeType;
     }
     }
 
 
     public override bool InitializeAndValidate(Document target)
     public override bool InitializeAndValidate(Document target)
     {
     {
         if (target.TryFindNode<VectorLayerNode>(TargetId, out var node))
         if (target.TryFindNode<VectorLayerNode>(TargetId, out var node))
         {
         {
-            // TODO: Add is identical check
+            if (IsIdentical(node.ShapeData, Data))
+            {
+                return false;
+            }
+
             originalData = (ShapeVectorData?)node.ShapeData?.Clone();
             originalData = (ShapeVectorData?)node.ShapeData?.Clone();
             return true;
             return true;
         }
         }
@@ -45,7 +51,7 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
         var node = target.FindNode<VectorLayerNode>(TargetId);
         var node = target.FindNode<VectorLayerNode>(TargetId);
 
 
         node.ShapeData = Data;
         node.ShapeData = Data;
-        
+
         RectD aabb = node.ShapeData.TransformedAABB.RoundOutwards();
         RectD aabb = node.ShapeData.TransformedAABB.RoundOutwards();
         aabb = aabb with { Size = new VecD(Math.Max(1, aabb.Size.X), Math.Max(1, aabb.Size.Y)) };
         aabb = aabb with { Size = new VecD(Math.Max(1, aabb.Size.X), Math.Max(1, aabb.Size.Y)) };
 
 
@@ -99,13 +105,46 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
         return new VectorShape_ChangeInfo(node.Id, affected);
         return new VectorShape_ChangeInfo(node.Id, affected);
     }
     }
 
 
+    private bool IsIdentical(ShapeVectorData? a, ShapeVectorData? b)
+    {
+        if (a is null && b is null)
+        {
+            return true;
+        }
+
+        if (a is null || b is null)
+        {
+            return false;
+        }
+
+        if (a.GetType() != b.GetType())
+        {
+            return false;
+        }
+
+        return a.Equals(b);
+    }
+
     public override bool IsMergeableWith(Change other)
     public override bool IsMergeableWith(Change other)
     {
     {
         if (other is SetShapeGeometry_UpdateableChange change)
         if (other is SetShapeGeometry_UpdateableChange change)
         {
         {
-            return change.TargetId == TargetId && change.Data is not TextVectorData; // text should not be merged into one change
+            return change.TargetId == TargetId &&
+                   !ChangeType.HasFlag(VectorShapeChangeType.GeometryData) && ChangeType.HasFlag(change.ChangeType) &&
+                   change.Data is not TextVectorData; // text should not be merged into one change
         }
         }
 
 
         return false;
         return false;
     }
     }
 }
 }
+
+[Flags]
+public enum VectorShapeChangeType
+{
+    GeometryData = 1,
+    Stroke = 2,
+    Fill = 4,
+    TransformationMatrix = 8,
+    OtherVisuals = 16,
+    All = ~0
+}

+ 38 - 16
src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs

@@ -135,17 +135,28 @@ public class DocumentChangeTracker : IDisposable
         }
         }
 
 
         List<IChangeInfo> changeInfos = new();
         List<IChangeInfo> changeInfos = new();
-        var changePacket = undoStack.Pop();
+        bool handledUserChange = false;
 
 
-        for (int i = changePacket.changes.Count - 1; i >= 0; i--)
+        (ActionSource source, List<Change> changes) changePacket;
+        do
         {
         {
-            changePacket.changes[i].Revert(document).Switch(
-                (None _) => { },
-                (IChangeInfo info) => changeInfos.Add(info),
-                (List<IChangeInfo> infos) => changeInfos.AddRange(infos));
-        }
+            changePacket = undoStack.Pop();
+            for (int i = changePacket.changes.Count - 1; i >= 0; i--)
+            {
+                changePacket.changes[i].Revert(document).Switch(
+                    (None _) => { },
+                    (IChangeInfo info) => changeInfos.Add(info),
+                    (List<IChangeInfo> infos) => changeInfos.AddRange(infos));
+            }
+
+            if (changePacket.source == ActionSource.User)
+                handledUserChange = true;
+
+            redoStack.Push(changePacket);
+        } while (undoStack.Count > 0 &&
+                 ((changePacket.source == ActionSource.Automated && !handledUserChange)
+                  || undoStack.Peek().source == ActionSource.Automated));
 
 
-        redoStack.Push(changePacket);
         return changeInfos;
         return changeInfos;
     }
     }
 
 
@@ -161,17 +172,28 @@ public class DocumentChangeTracker : IDisposable
         }
         }
 
 
         List<IChangeInfo> changeInfos = new();
         List<IChangeInfo> changeInfos = new();
-        var changePacket = redoStack.Pop();
+        (ActionSource source, List<Change> changes) changePacket;
+        bool handledUserChange = false;
 
 
-        for (int i = 0; i < changePacket.changes.Count; i++)
+        do
         {
         {
-            changePacket.changes[i].Apply(document, false, out _).Switch(
-                (None _) => { },
-                (IChangeInfo info) => changeInfos.Add(info),
-                (List<IChangeInfo> infos) => changeInfos.AddRange(infos));
-        }
+            changePacket = redoStack.Pop();
+            for (int i = 0; i < changePacket.changes.Count; i++)
+            {
+                changePacket.changes[i].Apply(document, false, out _).Switch(
+                    (None _) => { },
+                    (IChangeInfo info) => changeInfos.Add(info),
+                    (List<IChangeInfo> infos) => changeInfos.AddRange(infos));
+            }
+
+            if (changePacket.source == ActionSource.User)
+                handledUserChange = true;
+
+            undoStack.Push(changePacket);
+        } while (redoStack.Count > 0
+                 && ((changePacket.source == ActionSource.Automated && !handledUserChange)
+                     || redoStack.Peek().source == ActionSource.Automated));
 
 
-        undoStack.Push(changePacket);
         return changeInfos;
         return changeInfos;
     }
     }
 
 

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

@@ -44,7 +44,7 @@ internal class ChangeExecutionController
 
 
     public bool IsChangeOfTypeActive<T>() where T : IExecutorFeature
     public bool IsChangeOfTypeActive<T>() where T : IExecutorFeature
     {
     {
-        return currentSession is T sessionT && sessionT.IsFeatureEnabled(sessionT);
+        return currentSession is T sessionT && sessionT.IsFeatureEnabled<T>();
     }
     }
 
 
     public ExecutorType GetCurrentExecutorType()
     public ExecutorType GetCurrentExecutorType()

+ 65 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs

@@ -10,7 +10,10 @@ using PixiEditor.Models.Handlers.Toolbars;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument;
+using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changes.Vectors;
 using PixiEditor.ViewModels.Document.TransformOverlays;
 using PixiEditor.ViewModels.Document.TransformOverlays;
 using PixiEditor.Views.Overlays.TransformOverlay;
 using PixiEditor.Views.Overlays.TransformOverlay;
 using Color = Drawie.Backend.Core.ColorsImpl.Color;
 using Color = Drawie.Backend.Core.ColorsImpl.Color;
@@ -34,18 +37,26 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     protected RectD lastRect;
     protected RectD lastRect;
     protected double lastRadians;
     protected double lastRadians;
 
 
+    protected virtual bool DeleteLayerOnNoDraw => false;
+    protected virtual bool SelectLayerOnTap => false;
+    protected virtual Predicate<ILayerHandler> CanSelectLayer => x => true;
+
     private ShapeCorners initialCorners;
     private ShapeCorners initialCorners;
     private bool noMovement = true;
     private bool noMovement = true;
     protected IFillableShapeToolbar toolbar;
     protected IFillableShapeToolbar toolbar;
     private IColorsHandler? colorsVM;
     private IColorsHandler? colorsVM;
     private bool ignoreNextColorChange = false;
     private bool ignoreNextColorChange = false;
 
 
+    private bool preventSettingsChange = false;
+
     protected abstract bool UseGlobalUndo { get; }
     protected abstract bool UseGlobalUndo { get; }
     protected abstract bool ShowApplyButton { get; }
     protected abstract bool ShowApplyButton { get; }
 
 
     public override bool CanUndo => !UseGlobalUndo && document.TransformHandler.HasUndo;
     public override bool CanUndo => !UseGlobalUndo && document.TransformHandler.HasUndo;
     public override bool CanRedo => !UseGlobalUndo && document.TransformHandler.HasRedo;
     public override bool CanRedo => !UseGlobalUndo && document.TransformHandler.HasRedo;
 
 
+    protected virtual bool ApplyEachSettingsChange => false;
+
     public override ExecutionState Start()
     public override ExecutionState Start()
     {
     {
         if (base.Start() == ExecutionState.Error)
         if (base.Start() == ExecutionState.Error)
@@ -74,7 +85,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
 
             lastRect = new RectD(startDrawingPos, VecD.Zero);
             lastRect = new RectD(startDrawingPos, VecD.Zero);
 
 
-            document!.TransformHandler.ShowTransform(TransformMode, false, new ShapeCorners((RectD)lastRect.Inflate(1)),
+            document!.TransformHandler.ShowTransform(TransformMode, false, new ShapeCorners(lastRect),
                 false, UseGlobalUndo ? AddToUndo : null);
                 false, UseGlobalUndo ? AddToUndo : null);
             document.TransformHandler.ShowHandles = false;
             document.TransformHandler.ShowHandles = false;
             document.TransformHandler.IsSizeBoxEnabled = true;
             document.TransformHandler.IsSizeBoxEnabled = true;
@@ -115,7 +126,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     }
     }
 
 
     protected abstract void DrawShape(VecD currentPos, double rotationRad, bool firstDraw);
     protected abstract void DrawShape(VecD currentPos, double rotationRad, bool firstDraw);
-    protected abstract IAction SettingsChangedAction();
+    protected abstract IAction SettingsChangedAction(string name, object value);
     protected abstract IAction TransformMovedAction(ShapeData data, ShapeCorners corners);
     protected abstract IAction TransformMovedAction(ShapeData data, ShapeCorners corners);
     protected virtual bool InitShapeData(IReadOnlyShapeVectorData data) { return true; }
     protected virtual bool InitShapeData(IReadOnlyShapeVectorData data) { return true; }
     protected abstract bool CanEditShape(IStructureMemberHandler layer);
     protected abstract bool CanEditShape(IStructureMemberHandler layer);
@@ -201,8 +212,23 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
 
         ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
         ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
 
 
+        preventSettingsChange = true;
         toolbar.StrokeBrush = new SolidColorBrush(color.ToColor());
         toolbar.StrokeBrush = new SolidColorBrush(color.ToColor());
         toolbar.FillBrush = new SolidColorBrush(color.ToColor());
         toolbar.FillBrush = new SolidColorBrush(color.ToColor());
+        preventSettingsChange = false;
+
+        var layer = document.StructureHelper.Find(memberId);
+        if (layer is null)
+            return;
+
+        if (CanEditShape(layer))
+        {
+            internals!.ActionAccumulator.AddFinishedActions(
+                EndDrawAction(),
+                SettingsChangedAction("FillAndStroke", color),
+                EndDrawAction());
+            // TODO add to undo
+        }
     }
     }
 
 
     public override void OnSelectedObjectNudged(VecI distance)
     public override void OnSelectedObjectNudged(VecI distance)
@@ -340,14 +366,23 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
 
     public override void OnSettingsChanged(string name, object value)
     public override void OnSettingsChanged(string name, object value)
     {
     {
+        if (preventSettingsChange) return;
+
         var layer = document.StructureHelper.Find(memberId);
         var layer = document.StructureHelper.Find(memberId);
         if (layer is null)
         if (layer is null)
             return;
             return;
 
 
         if (CanEditShape(layer))
         if (CanEditShape(layer))
         {
         {
-            internals!.ActionAccumulator.AddActions(SettingsChangedAction());
-            // TODO add to undo
+            if (ApplyEachSettingsChange)
+            {
+                internals!.ActionAccumulator.AddFinishedActions(EndDrawAction(), SettingsChangedAction(name, value),
+                    EndDrawAction());
+            }
+            else
+            {
+                internals!.ActionAccumulator.AddActions(SettingsChangedAction(name, value));
+            }
         }
         }
     }
     }
 
 
@@ -362,6 +397,32 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
 
                 base.OnLeftMouseButtonUp(argsPositionOnCanvas);
                 base.OnLeftMouseButtonUp(argsPositionOnCanvas);
                 onEnded?.Invoke(this);
                 onEnded?.Invoke(this);
+
+                if (DeleteLayerOnNoDraw)
+                {
+                    if (lastRect.Size == VecD.Zero)
+                    {
+                        var member = document!.StructureHelper.Find(memberId);
+                        if (member is not null)
+                        {
+                            internals.ActionAccumulator.AddActions(ActionSource.Automated,
+                                new DeleteStructureMember_Action(memberId));
+                            //internals.ActionAccumulator.AddFinishedActions();
+                            document.TransformHandler.HideTransform();
+                        }
+                    }
+                }
+
+                if (SelectLayerOnTap)
+                {
+                    var layersUnderCursor = QueryLayers<ILayerHandler>(argsPositionOnCanvas);
+                    var firstValidLayer = layersUnderCursor.FirstOrDefault(x => CanSelectLayer(x));
+                    if (firstValidLayer != null)
+                    {
+                        document.Operations.SetSelectedMember(firstValidLayer.Id);
+                    }
+                }
+
                 return;
                 return;
             }
             }
         }
         }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/Features/IExecutorFeature.cs

@@ -2,5 +2,5 @@
 
 
 public interface IExecutorFeature
 public interface IExecutorFeature
 {
 {
-    public bool IsFeatureEnabled(IExecutorFeature feature);
+    public bool IsFeatureEnabled<T>();
 }
 }

+ 4 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -30,7 +30,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     protected bool drawOnMask;
     protected bool drawOnMask;
 
 
     protected VecD curPos;
     protected VecD curPos;
-    private bool startedDrawing = false;
+    protected bool startedDrawing = false;
     private T? toolViewModel;
     private T? toolViewModel;
     private IColorsHandler? colorsVM;
     private IColorsHandler? colorsVM;
     protected IShapeToolbar? toolbar;
     protected IShapeToolbar? toolbar;
@@ -112,7 +112,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     protected abstract bool InitShapeData(IReadOnlyLineData? data);
     protected abstract bool InitShapeData(IReadOnlyLineData? data);
     protected abstract IAction DrawLine(VecD pos);
     protected abstract IAction DrawLine(VecD pos);
     protected abstract IAction TransformOverlayMoved(VecD start, VecD end);
     protected abstract IAction TransformOverlayMoved(VecD start, VecD end);
-    protected abstract IAction[] SettingsChange();
+    protected abstract IAction[] SettingsChange(string name, object value);
     protected abstract IAction EndDraw();
     protected abstract IAction EndDraw();
 
 
     protected override void PrecisePositionChangeDrawingMode(VecD pos)
     protected override void PrecisePositionChangeDrawingMode(VecD pos)
@@ -210,7 +210,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
 
 
         ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
         ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
         toolbar!.StrokeBrush = new SolidColorBrush(color.ToColor());
         toolbar!.StrokeBrush = new SolidColorBrush(color.ToColor());
-        var colorChangedAction = SettingsChange();
+        var colorChangedAction = SettingsChange(nameof(IShapeToolbar.StrokeBrush), color);
         internals!.ActionAccumulator.AddActions(colorChangedAction);
         internals!.ActionAccumulator.AddActions(colorChangedAction);
     }
     }
 
 
@@ -225,7 +225,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
 
 
     public override void OnSettingsChanged(string name, object value)
     public override void OnSettingsChanged(string name, object value)
     {
     {
-        var colorChangedActions = SettingsChange();
+        var colorChangedActions = SettingsChange(name, value);
         if (ActiveMode == ShapeToolMode.Transform)
         if (ActiveMode == ShapeToolMode.Transform)
         {
         {
             internals!.ActionAccumulator.AddFinishedActions(colorChangedActions);
             internals!.ActionAccumulator.AddFinishedActions(colorChangedActions);

+ 4 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs

@@ -90,12 +90,13 @@ internal class PasteImageExecutor : UpdateableChangeExecutor, ITransformableExec
         internals!.ActionAccumulator.AddActions(new EndPasteImage_Action());
         internals!.ActionAccumulator.AddActions(new EndPasteImage_Action());
     }
     }
 
 
-    public bool IsFeatureEnabled(IExecutorFeature feature)
+    public bool IsFeatureEnabled<T>()
     {
     {
-        if (feature is ITransformableExecutor)
+        Type featureType = typeof(T);
+        if (featureType == typeof(ITransformableExecutor))
             return IsTransforming;
             return IsTransforming;
         
         
-        if (feature is IMidChangeUndoableExecutor)
+        if (featureType == typeof(IMidChangeUndoableExecutor))
             return true;
             return true;
 
 
         return false;
         return false;

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs

@@ -32,7 +32,7 @@ internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterElli
     protected override bool UseGlobalUndo => false;
     protected override bool UseGlobalUndo => false;
     protected override bool ShowApplyButton => true;
     protected override bool ShowApplyButton => true;
     protected override void DrawShape(VecD currentPos, double rotationRad, bool firstDraw) => DrawEllipseOrCircle(currentPos, rotationRad, firstDraw);
     protected override void DrawShape(VecD currentPos, double rotationRad, bool firstDraw) => DrawEllipseOrCircle(currentPos, rotationRad, firstDraw);
-    protected override IAction SettingsChangedAction()
+    protected override IAction SettingsChangedAction(string name, object value)
     {
     {
         return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokePaintable, FillPaintable, (float)StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokePaintable, FillPaintable, (float)StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
     }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterLineToolExecutor.cs

@@ -33,7 +33,7 @@ internal class RasterLineToolExecutor : LineExecutor<ILineToolHandler>
             (float)StrokeWidth, StrokePaintable, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
             (float)StrokeWidth, StrokePaintable, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
     }
 
 
-    protected override IAction[] SettingsChange()
+    protected override IAction[] SettingsChange(string name, object value)
     {
     {
         VecD dir = GetSignedDirection(startDrawingPos, curPos);
         VecD dir = GetSignedDirection(startDrawingPos, curPos);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs

@@ -41,7 +41,7 @@ internal class RasterRectangleToolExecutor : DrawableShapeToolExecutor<IRasterRe
     protected override void DrawShape(VecD currentPos, double rotationRad, bool first) =>
     protected override void DrawShape(VecD currentPos, double rotationRad, bool first) =>
         DrawRectangle(currentPos, rotationRad, first);
         DrawRectangle(currentPos, rotationRad, first);
 
 
-    protected override IAction SettingsChangedAction()
+    protected override IAction SettingsChangedAction(string name, object value)
     {
     {
         lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, (float)StrokeWidth, StrokePaintable, FillPaintable)
         lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, (float)StrokeWidth, StrokePaintable, FillPaintable)
         {
         {

+ 8 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs

@@ -37,6 +37,9 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
         get => activeMode;
         get => activeMode;
         set
         set
         {
         {
+            if (activeMode == value)
+                return;
+
             StopMode(activeMode);
             StopMode(activeMode);
             activeMode = value;
             activeMode = value;
             StartMode(activeMode);
             StartMode(activeMode);
@@ -249,19 +252,20 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
     public abstract bool CanUndo { get; }
     public abstract bool CanUndo { get; }
     public abstract bool CanRedo { get; }
     public abstract bool CanRedo { get; }
 
 
-    public virtual bool IsFeatureEnabled(IExecutorFeature feature)
+    public virtual bool IsFeatureEnabled<T>()
     {
     {
-        if (feature is ITransformableExecutor)
+        Type t = typeof(T);
+        if (t == typeof(ITransformableExecutor))
         {
         {
             return IsTransforming;
             return IsTransforming;
         }
         }
 
 
-        if (feature is IMidChangeUndoableExecutor)
+        if (t == typeof(IMidChangeUndoableExecutor))
         {
         {
             return ActiveMode == ShapeToolMode.Transform;
             return ActiveMode == ShapeToolMode.Transform;
         }
         }
 
 
-        if (feature is IDelayedColorSwapFeature)
+        if (t == typeof(IDelayedColorSwapFeature))
         {
         {
             return true;
             return true;
         }
         }

+ 2 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformReferenceLayerExecutor.cs

@@ -50,8 +50,8 @@ internal class TransformReferenceLayerExecutor : UpdateableChangeExecutor, ITran
         document.ReferenceLayerHandler.IsTransforming = false;
         document.ReferenceLayerHandler.IsTransforming = false;
     }
     }
 
 
-    public bool IsFeatureEnabled(IExecutorFeature feature)
+    public bool IsFeatureEnabled<T>()
     {
     {
-        return feature is ITransformableExecutor;
+        return typeof(T) == typeof(ITransformableExecutor);
     }
     }
 }
 }

+ 6 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/TransformSelectedExecutor.cs

@@ -13,6 +13,7 @@ using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels;
 using PixiEditor.ViewModels.Document.Nodes;
 using PixiEditor.ViewModels.Document.Nodes;
+using Type = System.Type;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
 #nullable enable
@@ -508,9 +509,11 @@ internal class TransformSelectedExecutor : UpdateableChangeExecutor, ITransforma
         }
         }
     }
     }
 
 
-    public bool IsFeatureEnabled(IExecutorFeature feature)
+    public bool IsFeatureEnabled<T>()
     {
     {
-        return feature is ITransformableExecutor && IsTransforming || feature is IMidChangeUndoableExecutor ||
-               feature is ITransformStoppedEvent || feature is ITransformDraggedEvent;
+        Type feature = typeof(T);
+        return feature == typeof(ITransformableExecutor) && IsTransforming ||
+               feature == typeof(IMidChangeUndoableExecutor) ||
+               feature == typeof(ITransformStoppedEvent) || feature == typeof(ITransformDraggedEvent);
     }
     }
 }
 }

+ 26 - 7
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs

@@ -8,8 +8,11 @@ using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+using PixiEditor.ChangeableDocument.Changes.Vectors;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
+using PixiEditor.Models.Handlers.Toolbars;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 
@@ -24,6 +27,13 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
     private Matrix3X3 lastMatrix = Matrix3X3.Identity;
     private Matrix3X3 lastMatrix = Matrix3X3.Identity;
 
 
     protected override bool AlignToPixels => false;
     protected override bool AlignToPixels => false;
+    protected override bool DeleteLayerOnNoDraw => true;
+    protected override bool SelectLayerOnTap => true;
+    protected override bool ApplyEachSettingsChange => true;
+
+    protected override Predicate<ILayerHandler> CanSelectLayer => x => x is IVectorLayerHandler vec
+                                                                       && vec.GetShapeData(vec.Document.AnimationHandler
+                                                                           .ActiveFrameTime) is IReadOnlyEllipseData;
 
 
     protected override bool InitShapeData(IReadOnlyShapeVectorData data)
     protected override bool InitShapeData(IReadOnlyShapeVectorData data)
     {
     {
@@ -69,11 +79,20 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
 
 
         lastRect = rect;
         lastRect = rect;
 
 
-        internals!.ActionAccumulator.AddActions(new SetShapeGeometry_Action(memberId, data));
+        internals!.ActionAccumulator.AddActions(new SetShapeGeometry_Action(memberId, data, VectorShapeChangeType.GeometryData));
     }
     }
 
 
-    protected override IAction SettingsChangedAction()
+    protected override IAction SettingsChangedAction(string name, object value)
     {
     {
+        VectorShapeChangeType changeType = name switch
+        {
+            nameof(IShapeToolbar.StrokeBrush) => VectorShapeChangeType.Stroke,
+            nameof(IShapeToolbar.ToolSize) => VectorShapeChangeType.Stroke,
+            nameof(IFillableShapeToolbar.FillBrush) => VectorShapeChangeType.Fill,
+            nameof(IShapeToolbar.AntiAliasing) => VectorShapeChangeType.OtherVisuals,
+            "FillAndStroke" => VectorShapeChangeType.Fill | VectorShapeChangeType.Stroke,
+            _ => VectorShapeChangeType.All
+        };
         return new SetShapeGeometry_Action(memberId,
         return new SetShapeGeometry_Action(memberId,
             new EllipseVectorData(firstCenter, firstRadius)
             new EllipseVectorData(firstCenter, firstRadius)
             {
             {
@@ -81,7 +100,7 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
                 FillPaintable = FillPaintable,
                 FillPaintable = FillPaintable,
                 StrokeWidth = (float)StrokeWidth,
                 StrokeWidth = (float)StrokeWidth,
                 TransformationMatrix = lastMatrix
                 TransformationMatrix = lastMatrix
-            });
+            }, changeType);
     }
     }
 
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
@@ -117,7 +136,7 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
         lastRect = rect;
         lastRect = rect;
         lastMatrix = matrix;
         lastMatrix = matrix;
 
 
-        return new SetShapeGeometry_Action(memberId, ellipseData);
+        return new SetShapeGeometry_Action(memberId, ellipseData, VectorShapeChangeType.GeometryData);
     }
     }
 
 
     protected override IAction EndDrawAction()
     protected override IAction EndDrawAction()
@@ -125,9 +144,9 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
         return new EndSetShapeGeometry_Action();
         return new EndSetShapeGeometry_Action();
     }
     }
 
 
-    public override bool IsFeatureEnabled(IExecutorFeature feature)
+    public override bool IsFeatureEnabled<T>()
     {
     {
-        if(feature is IMidChangeUndoableExecutor) return false;
-        return base.IsFeatureEnabled(feature);
+        if (typeof(T) == typeof(IMidChangeUndoableExecutor)) return false;
+        return base.IsFeatureEnabled<T>();
     }
     }
 }
 }

+ 53 - 11
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorLineToolExecutor.cs

@@ -5,11 +5,14 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Handlers.Tools;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changes.Vectors;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
+using PixiEditor.Models.Handlers;
+using PixiEditor.Models.Handlers.Toolbars;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 
-internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler> 
+internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler>
 {
 {
     private VecD startPoint;
     private VecD startPoint;
     private VecD endPoint;
     private VecD endPoint;
@@ -33,26 +36,64 @@ internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler>
     protected override IAction DrawLine(VecD pos)
     protected override IAction DrawLine(VecD pos)
     {
     {
         LineVectorData data = ConstructLineData(startDrawingPos, pos);
         LineVectorData data = ConstructLineData(startDrawingPos, pos);
-        
+
         startPoint = startDrawingPos;
         startPoint = startDrawingPos;
         endPoint = pos;
         endPoint = pos;
 
 
-        return new SetShapeGeometry_Action(memberId, data);
+        return new SetShapeGeometry_Action(memberId, data, VectorShapeChangeType.GeometryData);
     }
     }
 
 
     protected override IAction TransformOverlayMoved(VecD start, VecD end)
     protected override IAction TransformOverlayMoved(VecD start, VecD end)
     {
     {
         var data = ConstructLineData(start, end);
         var data = ConstructLineData(start, end);
-        
+
         startPoint = start;
         startPoint = start;
         endPoint = end;
         endPoint = end;
 
 
-        return new SetShapeGeometry_Action(memberId, data);
+        return new SetShapeGeometry_Action(memberId, data, VectorShapeChangeType.GeometryData);
     }
     }
 
 
-    protected override IAction[] SettingsChange()
+    protected IAction SettingsChanged(string name, object value)
     {
     {
-        return [TransformOverlayMoved(startPoint, endPoint), new EndSetShapeGeometry_Action()];
+        var data = ConstructLineData(startPoint, endPoint);
+
+        VectorShapeChangeType changeType = name switch
+        {
+            nameof(IShapeToolbar.StrokeBrush) => VectorShapeChangeType.Stroke,
+            nameof(IShapeToolbar.ToolSize) => VectorShapeChangeType.Stroke,
+            nameof(IShapeToolbar.AntiAliasing) => VectorShapeChangeType.OtherVisuals,
+            _ => VectorShapeChangeType.All
+        };
+
+        return new SetShapeGeometry_Action(memberId, data, changeType);
+    }
+
+    public override void OnLeftMouseButtonUp(VecD argsPositionOnCanvas)
+    {
+        base.OnLeftMouseButtonUp(argsPositionOnCanvas);
+
+        if (!startedDrawing)
+        {
+            var member = document!.StructureHelper.Find(memberId);
+            if (member is not null)
+            {
+                document.Operations.DeleteStructureMember(memberId);
+                document.TransformHandler.HideTransform();
+            }
+        }
+
+        var layersUnderCursor = QueryLayers<IVectorLayerHandler>(argsPositionOnCanvas);
+        var firstValidLayer = layersUnderCursor.FirstOrDefault(x =>
+            x.GetShapeData(document.AnimationHandler.ActiveFrameTime) is IReadOnlyLineData);
+        if (firstValidLayer != null)
+        {
+            document.Operations.SetSelectedMember(firstValidLayer.Id);
+        }
+    }
+
+    protected override IAction[] SettingsChange(string name, object value)
+    {
+        return [SettingsChanged(name, value), new EndSetShapeGeometry_Action()];
     }
     }
 
 
     protected override IAction EndDraw()
     protected override IAction EndDraw()
@@ -60,10 +101,11 @@ internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler>
         return new EndSetShapeGeometry_Action();
         return new EndSetShapeGeometry_Action();
     }
     }
 
 
-    public override bool IsFeatureEnabled(IExecutorFeature feature)
+    public override bool IsFeatureEnabled<T>()
     {
     {
-        if(feature is IMidChangeUndoableExecutor) return false;
-        
-        return base.IsFeatureEnabled(feature);
+        Type feature = typeof(T);
+        if (feature == typeof(IMidChangeUndoableExecutor)) return false;
+
+        return base.IsFeatureEnabled<T>();
     }
     }
 }
 }

+ 34 - 21
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs

@@ -12,6 +12,7 @@ using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changes.Vectors;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.DocumentModels.Public;
 using PixiEditor.Models.DocumentModels.Public;
@@ -98,9 +99,9 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
                 }
                 }
 
 
                 //below forces undo before starting new path
                 //below forces undo before starting new path
-               // internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action());
+                // internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action());
 
 
-               // internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(member.Id, ConstructShapeData(startingPath)));
+                // internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(member.Id, ConstructShapeData(startingPath)));
             }
             }
         }
         }
         else
         else
@@ -150,11 +151,11 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
             }
             }
         }
         }
     }
     }
-    
+
     private bool WholePathClosed()
     private bool WholePathClosed()
     {
     {
         EditableVectorPath editablePath = new EditableVectorPath(startingPath);
         EditableVectorPath editablePath = new EditableVectorPath(startingPath);
-        
+
         return editablePath.SubShapes.Count > 0 && editablePath.SubShapes.All(x => x.IsClosed);
         return editablePath.SubShapes.Count > 0 && editablePath.SubShapes.All(x => x.IsClosed);
     }
     }
 
 
@@ -176,7 +177,18 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
     {
     {
         if (document.PathOverlayHandler.IsActive)
         if (document.PathOverlayHandler.IsActive)
         {
         {
-            internals.ActionAccumulator.AddFinishedActions(new SetShapeGeometry_Action(member.Id, ConstructShapeData(startingPath)), new EndSetShapeGeometry_Action());
+            VectorShapeChangeType changeType = name switch
+            {
+                nameof(IFillableShapeToolbar.Fill) => VectorShapeChangeType.Fill,
+                nameof(IShapeToolbar.StrokeBrush) => VectorShapeChangeType.Stroke,
+                nameof(IShapeToolbar.ToolSize) => VectorShapeChangeType.Stroke,
+                nameof(IShapeToolbar.AntiAliasing) => VectorShapeChangeType.OtherVisuals,
+                _ => VectorShapeChangeType.All
+            };
+
+            internals.ActionAccumulator.AddFinishedActions(
+                new SetShapeGeometry_Action(member.Id, ConstructShapeData(startingPath), changeType),
+                new EndSetShapeGeometry_Action());
         }
         }
     }
     }
 
 
@@ -193,12 +205,12 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
     private void AddToUndo(VectorPath path)
     private void AddToUndo(VectorPath path)
     {
     {
         internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action(),
         internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action(),
-            new SetShapeGeometry_Action(member.Id, ConstructShapeData(path)), new EndSetShapeGeometry_Action());
+            new SetShapeGeometry_Action(member.Id, ConstructShapeData(path), VectorShapeChangeType.GeometryData), new EndSetShapeGeometry_Action());
     }
     }
 
 
     private PathVectorData ConstructShapeData(VectorPath? path)
     private PathVectorData ConstructShapeData(VectorPath? path)
     {
     {
-        if(path is null)
+        if (path is null)
         {
         {
             return new PathVectorData(new VectorPath() { FillType = (PathFillType)vectorPathToolHandler.FillMode })
             return new PathVectorData(new VectorPath() { FillType = (PathFillType)vectorPathToolHandler.FillMode })
             {
             {
@@ -210,7 +222,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
                 StrokeLineJoin = vectorPathToolHandler.StrokeLineJoin
                 StrokeLineJoin = vectorPathToolHandler.StrokeLineJoin
             };
             };
         }
         }
-        
+
         return new PathVectorData(new VectorPath(path) { FillType = (PathFillType)vectorPathToolHandler.FillMode })
         return new PathVectorData(new VectorPath(path) { FillType = (PathFillType)vectorPathToolHandler.FillMode })
         {
         {
             StrokeWidth = (float)toolbar.ToolSize,
             StrokeWidth = (float)toolbar.ToolSize,
@@ -227,19 +239,17 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
         if (document.PathOverlayHandler.IsActive)
         if (document.PathOverlayHandler.IsActive)
         {
         {
             startingPath = path;
             startingPath = path;
-            internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(member.Id, ConstructShapeData(startingPath)));
+            internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(member.Id,
+                ConstructShapeData(startingPath), VectorShapeChangeType.GeometryData));
         }
         }
     }
     }
 
 
-    public bool IsFeatureEnabled(IExecutorFeature feature)
+    public bool IsFeatureEnabled<T>()
     {
     {
-        return feature switch
-        {
-            IPathExecutorFeature _ => true,
-            IMidChangeUndoableExecutor _ => true,
-            ITransformableExecutor _ => true,
-            _ => false
-        };
+        Type feature = typeof(T);
+        return feature == typeof(IMidChangeUndoableExecutor)
+               || feature == typeof(ITransformableExecutor)
+               || feature == typeof(IPathExecutorFeature);
     }
     }
 
 
     protected void HighlightSnapping(string? snapX, string? snapY)
     protected void HighlightSnapping(string? snapX, string? snapY)
@@ -259,7 +269,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
 
 
         return shapeData is not IReadOnlyPathData pathData || pathData.Path.IsClosed;
         return shapeData is not IReadOnlyPathData pathData || pathData.Path.IsClosed;
     }
     }
-    
+
     private void ApplySettings(PathVectorData pathData)
     private void ApplySettings(PathVectorData pathData)
     {
     {
         toolbar.ToolSize = pathData.StrokeWidth;
         toolbar.ToolSize = pathData.StrokeWidth;
@@ -267,8 +277,11 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
         toolbar.ToolSize = pathData.StrokeWidth;
         toolbar.ToolSize = pathData.StrokeWidth;
         toolbar.Fill = pathData.Fill;
         toolbar.Fill = pathData.Fill;
         toolbar.FillBrush = pathData.FillPaintable.ToBrush();
         toolbar.FillBrush = pathData.FillPaintable.ToBrush();
-        toolbar.GetSetting<EnumSettingViewModel<VectorPathFillType>>(nameof(VectorPathToolViewModel.FillMode)).Value = (VectorPathFillType)pathData.Path.FillType;
-        toolbar.GetSetting<EnumSettingViewModel<StrokeCap>>(nameof(VectorPathToolViewModel.StrokeLineCap)).Value = pathData.StrokeLineCap;
-        toolbar.GetSetting<EnumSettingViewModel<StrokeJoin>>(nameof(VectorPathToolViewModel.StrokeLineJoin)).Value = pathData.StrokeLineJoin;
+        toolbar.GetSetting<EnumSettingViewModel<VectorPathFillType>>(nameof(VectorPathToolViewModel.FillMode)).Value =
+            (VectorPathFillType)pathData.Path.FillType;
+        toolbar.GetSetting<EnumSettingViewModel<StrokeCap>>(nameof(VectorPathToolViewModel.StrokeLineCap)).Value =
+            pathData.StrokeLineCap;
+        toolbar.GetSetting<EnumSettingViewModel<StrokeJoin>>(nameof(VectorPathToolViewModel.StrokeLineJoin)).Value =
+            pathData.StrokeLineJoin;
     }
     }
 }
 }

+ 29 - 7
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorRectangleToolExecutor.cs

@@ -8,8 +8,11 @@ using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+using PixiEditor.ChangeableDocument.Changes.Vectors;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
+using PixiEditor.Models.Handlers.Toolbars;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 
 
@@ -25,6 +28,14 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
 
 
     protected override bool AlignToPixels => false;
     protected override bool AlignToPixels => false;
 
 
+    protected override bool DeleteLayerOnNoDraw => true;
+    protected override bool SelectLayerOnTap => true;
+    protected override bool ApplyEachSettingsChange => true;
+
+    protected override Predicate<ILayerHandler> CanSelectLayer => x => x is IVectorLayerHandler vec
+                                                                       && vec.GetShapeData(vec.Document.AnimationHandler
+                                                                           .ActiveFrameTime) is IReadOnlyRectangleData;
+
     protected override bool InitShapeData(IReadOnlyShapeVectorData data)
     protected override bool InitShapeData(IReadOnlyShapeVectorData data)
     {
     {
         if (data is not RectangleVectorData rectData)
         if (data is not RectangleVectorData rectData)
@@ -72,11 +83,22 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
 
 
         lastRect = rect;
         lastRect = rect;
 
 
-        internals!.ActionAccumulator.AddActions(new SetShapeGeometry_Action(memberId, data));
+        internals!.ActionAccumulator.AddActions(new SetShapeGeometry_Action(memberId, data,
+            VectorShapeChangeType.GeometryData));
     }
     }
 
 
-    protected override IAction SettingsChangedAction()
+    protected override IAction SettingsChangedAction(string name, object value)
     {
     {
+        VectorShapeChangeType changeType = name switch
+        {
+            nameof(IFillableShapeToolbar.Fill) => VectorShapeChangeType.Fill,
+            nameof(IFillableShapeToolbar.FillBrush) => VectorShapeChangeType.Fill,
+            nameof(IShapeToolbar.StrokeBrush) => VectorShapeChangeType.Stroke,
+            nameof(IShapeToolbar.ToolSize) => VectorShapeChangeType.Stroke,
+            nameof(IShapeToolbar.AntiAliasing) => VectorShapeChangeType.OtherVisuals,
+            "FillAndStroke" => VectorShapeChangeType.Fill | VectorShapeChangeType.Stroke,
+            _ => VectorShapeChangeType.All
+        };
         return new SetShapeGeometry_Action(memberId,
         return new SetShapeGeometry_Action(memberId,
             new RectangleVectorData(firstCenter, firstSize)
             new RectangleVectorData(firstCenter, firstSize)
             {
             {
@@ -84,7 +106,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
                 FillPaintable = FillPaintable,
                 FillPaintable = FillPaintable,
                 StrokeWidth = (float)StrokeWidth,
                 StrokeWidth = (float)StrokeWidth,
                 TransformationMatrix = lastMatrix
                 TransformationMatrix = lastMatrix
-            });
+            }, changeType);
     }
     }
 
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
@@ -124,7 +146,7 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
 
 
         lastMatrix = matrix;
         lastMatrix = matrix;
 
 
-        return new SetShapeGeometry_Action(memberId, newData);
+        return new SetShapeGeometry_Action(memberId, newData, VectorShapeChangeType.GeometryData);
     }
     }
 
 
     protected override IAction EndDrawAction()
     protected override IAction EndDrawAction()
@@ -132,9 +154,9 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
         return new EndSetShapeGeometry_Action();
         return new EndSetShapeGeometry_Action();
     }
     }
 
 
-    public override bool IsFeatureEnabled(IExecutorFeature feature)
+    public override bool IsFeatureEnabled<T>()
     {
     {
-        if (feature is IMidChangeUndoableExecutor) return false;
-        return base.IsFeatureEnabled(feature);
+        if (typeof(T) == typeof(IMidChangeUndoableExecutor)) return false;
+        return base.IsFeatureEnabled<T>();
     }
     }
 }
 }

+ 65 - 12
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs

@@ -8,6 +8,7 @@ using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Changes.Vectors;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
@@ -32,7 +33,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     private Font? cachedFont;
     private Font? cachedFont;
     private bool isListeningForValidLayer;
     private bool isListeningForValidLayer;
     private VectorPath? onPath;
     private VectorPath? onPath;
-    
+
     private List<Font> fontsToDispose = new();
     private List<Font> fontsToDispose = new();
 
 
     public override bool BlocksOtherActions => false;
     public override bool BlocksOtherActions => false;
@@ -61,8 +62,16 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             return ExecutionState.Success;
             return ExecutionState.Success;
         }
         }
 
 
+        IColorsHandler colorsVM = GetHandler<IColorsHandler>();
+
         isListeningForValidLayer = false;
         isListeningForValidLayer = false;
         var shape = layerHandler.GetShapeData(document.AnimationHandler.ActiveFrameBindable);
         var shape = layerHandler.GetShapeData(document.AnimationHandler.ActiveFrameBindable);
+
+        if (toolbar.SyncWithPrimaryColor)
+        {
+            toolbar.FillBrush = new SolidColorBrush(colorsVM.PrimaryColor.ToColor());
+        }
+
         if (shape is TextVectorData textData)
         if (shape is TextVectorData textData)
         {
         {
             document.TextOverlayHandler.Show(textData.Text, textData.Position, textData.Font,
             document.TextOverlayHandler.Show(textData.Text, textData.Position, textData.Font,
@@ -129,6 +138,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
                     document.TextOverlayHandler.Show(lastText, position, toolbar.ConstructFont(), lastMatrix,
                     document.TextOverlayHandler.Show(lastText, position, toolbar.ConstructFont(), lastMatrix,
                         toolbar.Spacing);
                         toolbar.Spacing);
                 }
                 }
+
                 document.TextOverlayHandler.SetCursorPosition(args.PositionOnCanvas);
                 document.TextOverlayHandler.SetCursorPosition(args.PositionOnCanvas);
             }, false);
             }, false);
     }
     }
@@ -142,7 +152,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     {
     {
         internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action());
         internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action());
         document.TextOverlayHandler.Hide();
         document.TextOverlayHandler.Hide();
-        
+
         foreach (var font in fontsToDispose)
         foreach (var font in fontsToDispose)
         {
         {
             if (font != null && !font.IsDisposed)
             if (font != null && !font.IsDisposed)
@@ -150,7 +160,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
                 font.Dispose();
                 font.Dispose();
             }
             }
         }
         }
-        
+
         fontsToDispose.Clear();
         fontsToDispose.Clear();
     }
     }
 
 
@@ -158,7 +168,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     {
     {
         var constructedText = ConstructTextData(text);
         var constructedText = ConstructTextData(text);
         internals.ActionAccumulator.AddFinishedActions(
         internals.ActionAccumulator.AddFinishedActions(
-            new SetShapeGeometry_Action(selectedMember.Id, constructedText),
+            new SetShapeGeometry_Action(selectedMember.Id, constructedText, VectorShapeChangeType.GeometryData),
             new EndSetShapeGeometry_Action(),
             new EndSetShapeGeometry_Action(),
             new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering));
             new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering));
         lastText = text;
         lastText = text;
@@ -167,6 +177,8 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
 
 
     public override void OnSettingsChanged(string name, object value)
     public override void OnSettingsChanged(string name, object value)
     {
     {
+        if (!document.TextOverlayHandler.IsActive) return;
+
         if (isListeningForValidLayer)
         if (isListeningForValidLayer)
         {
         {
             return;
             return;
@@ -185,16 +197,55 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
                 cachedFont = toolbar.ConstructFont();
                 cachedFont = toolbar.ConstructFont();
             }
             }
 
 
-            document.TextOverlayHandler.Font.Size = toolbar.FontSize;
+            if (document.TextOverlayHandler.Font != null)
+            {
+                document.TextOverlayHandler.Font.Size = toolbar.FontSize;
+            }
+
             cachedFont.Size = toolbar.FontSize;
             cachedFont.Size = toolbar.FontSize;
             cachedFont.Bold = toolbar.Bold;
             cachedFont.Bold = toolbar.Bold;
             cachedFont.Italic = toolbar.Italic;
             cachedFont.Italic = toolbar.Italic;
         }
         }
 
 
+        VectorShapeChangeType changeType = name switch
+        {
+            nameof(ITextToolbar.Fill) => VectorShapeChangeType.Fill,
+            nameof(ITextToolbar.FillBrush) => VectorShapeChangeType.Fill,
+            nameof(ITextToolbar.StrokeBrush) => VectorShapeChangeType.Stroke,
+            nameof(ITextToolbar.ToolSize) => VectorShapeChangeType.GeometryData,
+            nameof(ITextToolbar.Spacing) => VectorShapeChangeType.GeometryData,
+            nameof(ITextToolbar.AntiAliasing) => VectorShapeChangeType.OtherVisuals,
+            nameof(ITextToolbar.ForceLowDpiRendering) => VectorShapeChangeType.OtherVisuals,
+            _ => VectorShapeChangeType.OtherVisuals
+        };
+
         var constructedText = ConstructTextData(lastText);
         var constructedText = ConstructTextData(lastText);
-        internals.ActionAccumulator.AddActions(
-            new SetShapeGeometry_Action(selectedMember.Id, constructedText),
-            new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering));
+        var layer = document.StructureHelper.Find(selectedMember.Id);
+        TextVectorData previousData =
+            (layer as IVectorLayerHandler).GetShapeData(document.AnimationHandler.ActiveFrameTime) as TextVectorData;
+        FontEdging previousEdging = constructedText.Font.Edging;
+        bool previousAntiAlias = constructedText.AntiAlias;
+        bool previousSubpixel = constructedText.Font.SubPixel;
+
+        if (previousData != null)
+        {
+            constructedText.AntiAlias = previousData.AntiAlias;
+            constructedText.Font.Edging = previousData.Font.Edging;
+            constructedText.Font.SubPixel = previousData.Font.SubPixel;
+        }
+
+        bool equals = constructedText.Equals(previousData);
+
+        constructedText.AntiAlias = previousAntiAlias;
+        constructedText.Font.Edging = previousEdging;
+        constructedText.Font.SubPixel = previousSubpixel;
+
+        if (!equals)
+        {
+            internals.ActionAccumulator.AddActions(
+                new SetShapeGeometry_Action(selectedMember.Id, constructedText, changeType),
+                new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering));
+        }
 
 
         document.TextOverlayHandler.Font = constructedText.Font;
         document.TextOverlayHandler.Font = constructedText.Font;
         document.TextOverlayHandler.Spacing = toolbar.Spacing;
         document.TextOverlayHandler.Spacing = toolbar.Spacing;
@@ -233,10 +284,10 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
 
 
         var constructedText = ConstructTextData(lastText);
         var constructedText = ConstructTextData(lastText);
         internals.ActionAccumulator.AddFinishedActions(
         internals.ActionAccumulator.AddFinishedActions(
-            new SetShapeGeometry_Action(selectedMember.Id, constructedText),
+            new SetShapeGeometry_Action(selectedMember.Id, constructedText, VectorShapeChangeType.GeometryData),
             new EndSetShapeGeometry_Action(),
             new EndSetShapeGeometry_Action(),
             new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering),
             new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering),
-            new SetShapeGeometry_Action(firstValidLayer.Id, newShape),
+            new SetShapeGeometry_Action(firstValidLayer.Id, newShape, VectorShapeChangeType.GeometryData),
             new EndSetShapeGeometry_Action());
             new EndSetShapeGeometry_Action());
     }
     }
 
 
@@ -266,6 +317,8 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             Stroke = toolbar.StrokeBrush.ToPaintable(),
             Stroke = toolbar.StrokeBrush.ToPaintable(),
             TransformationMatrix = lastMatrix,
             TransformationMatrix = lastMatrix,
             Font = cachedFont,
             Font = cachedFont,
+            Bold = toolbar.Bold,
+            Italic = toolbar.Italic,
             Spacing = toolbar.Spacing,
             Spacing = toolbar.Spacing,
             AntiAlias = toolbar.AntiAliasing,
             AntiAlias = toolbar.AntiAliasing,
             Path = onPath,
             Path = onPath,
@@ -273,8 +326,8 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
         };
         };
     }
     }
 
 
-    bool IExecutorFeature.IsFeatureEnabled(IExecutorFeature feature)
+    bool IExecutorFeature.IsFeatureEnabled<T>()
     {
     {
-        return feature is ITextOverlayEvents || feature is IQuickToolSwitchable;
+        return typeof(T) == typeof(ITextOverlayEvents) || typeof(T) == typeof(IQuickToolSwitchable);
     }
     }
 }
 }

+ 1 - 1
src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs

@@ -639,7 +639,7 @@ internal class TextOverlay : Overlay
 
 
     private void UpdateGlyphs()
     private void UpdateGlyphs()
     {
     {
-        if (Font == null) return;
+        if (Font == null || Font.IsDisposed) return;
 
 
         richText = new(Text);
         richText = new(Text);
         richText.Spacing = Spacing;
         richText.Spacing = Spacing;