Przeglądaj źródła

fixed symbols, size unit and blinker

flabbet 7 miesięcy temu
rodzic
commit
c325f8a8a4

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 72558508e77873d12fa4c89289cac586564b0276
+Subproject commit dc510dbaf4de99e4c48746aaca5d593078cea480

+ 14 - 12
src/PixiEditor.ChangeableDocument/Changes/Vectors/SetShapeGeometry_UpdateableChange.cs

@@ -12,7 +12,7 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
     public ShapeVectorData Data { get; set; }
 
     private ShapeVectorData? originalData;
-    
+
     private AffectedArea lastAffectedArea;
 
     [GenerateUpdateableChangeActions]
@@ -42,13 +42,15 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
     {
         var node = target.FindNode<VectorLayerNode>(TargetId);
-        if(node.ShapeData is IDisposable disposable)
+        IDisposable? toDispose = null;
+        if (node.ShapeData is IDisposable disposable)
         {
-            disposable.Dispose();
+            toDispose = disposable;
         }
-        
-        node.ShapeData = Data;
 
+        node.ShapeData = Data;
+        toDispose?.Dispose();
+        
         RectD aabb = node.ShapeData.TransformedAABB.RoundOutwards();
         aabb = aabb with { Size = new VecD(Math.Max(1, aabb.Size.X), Math.Max(1, aabb.Size.Y)) };
 
@@ -56,14 +58,14 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
             (RectI)aabb, ChunkyImage.FullChunkSize));
 
         var tmp = new AffectedArea(affected);
-        
+
         if (lastAffectedArea.Chunks != null)
         {
             affected.UnionWith(lastAffectedArea);
         }
-        
+
         lastAffectedArea = tmp;
-        
+
         return new VectorShape_ChangeInfo(node.Id, affected);
     }
 
@@ -73,7 +75,7 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
         ignoreInUndo = false;
         var node = target.FindNode<VectorLayerNode>(TargetId);
         node.ShapeData = Data;
-        
+
         RectD aabb = node.ShapeData.TransformedAABB.RoundOutwards();
         aabb = aabb with { Size = new VecD(Math.Max(1, aabb.Size.X), Math.Max(1, aabb.Size.Y)) };
 
@@ -89,12 +91,12 @@ internal class SetShapeGeometry_UpdateableChange : InterruptableUpdateableChange
         node.ShapeData = originalData;
 
         AffectedArea affected = new AffectedArea();
-        
+
         if (node.ShapeData != null)
-        { 
+        {
             RectD aabb = node.ShapeData.TransformedAABB.RoundOutwards();
             aabb = aabb with { Size = new VecD(Math.Max(1, aabb.Size.X), Math.Max(1, aabb.Size.Y)) };
-         
+
             affected = new AffectedArea(OperationHelper.FindChunksTouchingRectangle(
                 (RectI)aabb, ChunkyImage.FullChunkSize));
         }

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

@@ -837,5 +837,6 @@
   "COLOR_NODE": "Color",
   "CONVERT_TO_CURVE": "Convert to curve",
   "CONVERT_TO_CURVE_DESCRIPTIVE": "Convert selected vector layer to a curve/path",
-  "FONT_FILES": "Font Files"
+  "FONT_FILES": "Font Files",
+  "UNIT_PT": "pt"
 }

+ 9 - 5
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs

@@ -48,14 +48,14 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
         var shape = layerHandler.GetShapeData(document.AnimationHandler.ActiveFrameBindable);
         if (shape is TextVectorData textData)
         {
-            document.TextOverlayHandler.Show(textData.Text, textData.Position, textData.Font);
+            document.TextOverlayHandler.Show(textData.Text, textData.Position, textData.Font, textData.TransformationMatrix);
             lastText = textData.Text;
             position = textData.Position;
             lastMatrix = textData.TransformationMatrix;
         }
         else if (shape is null)
         {
-            document.TextOverlayHandler.Show("", controller.LastPrecisePosition, toolbar.ConstructFont());
+            document.TextOverlayHandler.Show("", controller.LastPrecisePosition, toolbar.ConstructFont(), Matrix3X3.Identity);
             lastText = "";
             position = controller.LastPrecisePosition;
         }
@@ -75,8 +75,10 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
 
     public void OnTextChanged(string text)
     {
-        internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(selectedMember.Id, ConstructTextData(text)));
+        var constructedText = ConstructTextData(text);
+        internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(selectedMember.Id, constructedText));
         lastText = text;
+        document.TextOverlayHandler.Font = constructedText.Font;
     }
 
     public override void OnSettingsChanged(string name, object value)
@@ -86,8 +88,10 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             document.TextOverlayHandler.Font = toolbar.ConstructFont();
         }
         
-        internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(selectedMember.Id,
-            ConstructTextData(lastText)));
+        var constructedText = ConstructTextData(lastText);
+        internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(selectedMember.Id, constructedText));
+        
+        document.TextOverlayHandler.Font = constructedText.Font;
     }
 
     public override void OnColorChanged(Color color, bool primary)

+ 3 - 2
src/PixiEditor/Models/Handlers/ITextOverlayHandler.cs

@@ -1,11 +1,12 @@
-using Drawie.Backend.Core.Text;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Text;
 using Drawie.Numerics;
 
 namespace PixiEditor.Models.Handlers;
 
 public interface ITextOverlayHandler : IHandler
 {
-    public void Show(string text, VecD position, Font font);
+    public void Show(string text, VecD position, Font font, Matrix3X3 matrix);
     public void Hide();
     public Font Font { get; set; }
     public VecD Position { get; set; }

+ 14 - 2
src/PixiEditor/ViewModels/Document/TransformOverlays/TextOverlayViewModel.cs

@@ -1,4 +1,5 @@
 using CommunityToolkit.Mvvm.ComponentModel;
+using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Text;
 using Drawie.Numerics;
 using PixiEditor.Helpers.UI;
@@ -13,6 +14,7 @@ internal class TextOverlayViewModel : ObservableObject, ITextOverlayHandler
     private VecD position;
     private Font font;
     private ExecutionTrigger<string> requestEditTextTrigger;
+    private Matrix3X3 matrix = Matrix3X3.Identity;
 
     public event Action<string>? TextChanged;
 
@@ -52,6 +54,12 @@ internal class TextOverlayViewModel : ObservableObject, ITextOverlayHandler
         get => requestEditTextTrigger;
         set => SetProperty(ref requestEditTextTrigger, value);
     }
+    
+    public Matrix3X3 Matrix
+    {
+        get => matrix;
+        set => SetProperty(ref matrix, value);
+    }
 
     public TextOverlayViewModel()
     {
@@ -59,17 +67,21 @@ internal class TextOverlayViewModel : ObservableObject, ITextOverlayHandler
     }
 
 
-    public void Show(string text, VecD position, Font font)
+    public void Show(string text, VecD position, Font font, Matrix3X3 matrix)
     {
-        IsActive = true;
         Font = font;
         Position = position;
         Text = text;
+        Matrix = matrix;
+        IsActive = true;
         RequestEditTextTrigger.Execute(this, text);
     }
 
     public void Hide()
     {
         IsActive = false;
+        Font = null!;
+        Position = default;
+        Text = string.Empty;
     }
 }

+ 12 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Settings/SizeSettingViewModel.cs

@@ -12,8 +12,9 @@ internal sealed class SizeSettingViewModel : Setting<double>
     private double min;
     private double max;
     private int decimalPlaces;
+    private string unit = "px";
     
-    public SizeSettingViewModel(string name, string label = null, double min = 1, double max = double.PositiveInfinity, int decimalPlaces = 0)
+    public SizeSettingViewModel(string name, string label = null, double min = 1, double max = double.PositiveInfinity, int decimalPlaces = 0, string unit = "px")
         : base(name)
     {
         Label = label;
@@ -22,6 +23,7 @@ internal sealed class SizeSettingViewModel : Setting<double>
         this.min = min;
         this.max = max;
         this.decimalPlaces = decimalPlaces;
+        this.unit = unit;
     }
 
     public bool IsEnabled
@@ -59,4 +61,13 @@ internal sealed class SizeSettingViewModel : Setting<double>
             SetProperty(ref decimalPlaces, value);
         }
     }
+    
+    public string Unit
+    {
+        get => unit;
+        set
+        {
+            SetProperty(ref unit, value);
+        }
+    }
 }

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

@@ -1,4 +1,5 @@
 using Drawie.Backend.Core.Text;
+using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Models.Handlers.Toolbars;
 using PixiEditor.ViewModels.Tools.ToolSettings.Settings;
 
@@ -33,7 +34,8 @@ internal class TextToolbar : FillableShapeToolbar, ITextToolbar
     public TextToolbar()
     {
         AddSetting(new FontFamilySettingViewModel(nameof(FontFamily), "FONT_LABEL"));
-        var sizeSetting = new SizeSettingViewModel(nameof(FontSize), "FONT_SIZE_LABEL") { Value = 12 };
+        var sizeSetting = new SizeSettingViewModel(nameof(FontSize), "FONT_SIZE_LABEL", unit: new LocalizedString("UNIT_PT")) 
+            { Value = 12 };
         AddSetting(sizeSetting);
     }
 

+ 6 - 0
src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs

@@ -477,12 +477,18 @@ internal class ViewportOverlays
         {
             Source = Viewport, Path = "Document.TextOverlayViewModel.RequestEditTextTrigger", Mode = BindingMode.OneWay
         };
+        
+        Binding matrixBinding = new()
+        {
+            Source = Viewport, Path = "Document.TextOverlayViewModel.Matrix", Mode = BindingMode.OneWay
+        };
 
         textOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         textOverlay.Bind(TextOverlay.TextProperty, textBinding);
         textOverlay.Bind(TextOverlay.PositionProperty, positionBinding);
         textOverlay.Bind(TextOverlay.FontProperty, fontBinding);
         textOverlay.Bind(TextOverlay.RequestEditTextProperty, requestEditTextBinding);
+        textOverlay.Bind(TextOverlay.MatrixProperty, matrixBinding);
     }
 }
 

+ 2 - 2
src/PixiEditor/Views/Overlays/Overlay.cs

@@ -146,7 +146,7 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
     
     public void KeyPressed(KeyEventArgs args)
     {
-        OnKeyPressed(args.Key, args.KeyModifiers);
+        OnKeyPressed(args.Key, args.KeyModifiers, args.KeySymbol);
         KeyPressedOverlay?.Invoke(args.Key, args.KeyModifiers);
     }
 
@@ -276,7 +276,7 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
     {
     }
     
-    protected virtual void OnKeyPressed(Key key, KeyModifiers keyModifiers)
+    protected virtual void OnKeyPressed(Key key, KeyModifiers keyModifiers, string? keySymbol)
     {
     }
     

+ 1 - 1
src/PixiEditor/Views/Overlays/PathOverlay/VectorPathOverlay.cs

@@ -357,7 +357,7 @@ public class VectorPathOverlay : Overlay
         args.Handled = true;
     }
 
-    protected override void OnKeyPressed(Key key, KeyModifiers keyModifiers)
+    protected override void OnKeyPressed(Key key, KeyModifiers keyModifiers, string? symbol)
     {
         if (key == Key.Delete)
         {

+ 8 - 14
src/PixiEditor/Views/Overlays/TextOverlay/Blinker.cs

@@ -13,33 +13,27 @@ internal class Blinker : IDisposable
     public VecF[] GlyphPositions { get; set; }
     public VecD Offset { get; set; }
     public float[] GlyphWidths { get; set; }
+    public float BlinkerWidth { get; set; } = 1;
+
+    private Paint paint = new Paint() { Color = Colors.White, Style = PaintStyle.StrokeAndFill, StrokeWidth = 1 };
 
-    private Paint paint = new Paint() { Color = Colors.Black, Style = PaintStyle.StrokeAndFill, StrokeWidth = 1};
-    
     public void Render(Canvas canvas)
     {
         if (GlyphPositions.Length == 0)
         {
             return;
         }
-        
-        int clampedBlinkerPosition = Math.Clamp(BlinkerPosition, 0, GlyphPositions.Length);
 
-        VecF glyphPosition;
-        if (clampedBlinkerPosition == GlyphPositions.Length)
-        {
-            glyphPosition = GlyphPositions[^1];   
-            glyphPosition += new VecF(GlyphWidths[^1], 0);
-        }
-        else
-        {
-            glyphPosition = GlyphPositions[clampedBlinkerPosition];
-        }
+        int clampedBlinkerPosition = Math.Clamp(BlinkerPosition, 0, GlyphPositions.Length - 1);
+
+        var glyphPosition = GlyphPositions[clampedBlinkerPosition];
 
         var glyphHeight = FontSize;
 
         var x = glyphPosition.X + Offset.X;
         var y = glyphPosition.Y + Offset.Y;
+        
+        paint.StrokeWidth = BlinkerWidth;
 
         VecD from = new VecD(x, y);
         VecD to = new VecD(x, y - glyphHeight);

+ 57 - 28
src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Input;
 using Avalonia.Threading;
+using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Text;
 using Drawie.Numerics;
 using PixiEditor.Helpers.UI;
@@ -41,7 +42,7 @@ internal class TextOverlay : Overlay
     }
 
     public static readonly StyledProperty<int> CursorPositionProperty = AvaloniaProperty.Register<TextOverlay, int>(
-        nameof(CursorPosition));
+        nameof(CursorPosition), coerce: ClampValue);
 
     public int CursorPosition
     {
@@ -58,6 +59,15 @@ internal class TextOverlay : Overlay
         set => SetValue(SelectionLengthProperty, value);
     }
 
+    public static readonly StyledProperty<Matrix3X3> MatrixProperty = AvaloniaProperty.Register<TextOverlay, Matrix3X3>(
+        nameof(Matrix), Matrix3X3.Identity);
+
+    public Matrix3X3 Matrix
+    {
+        get => GetValue(MatrixProperty);
+        set => SetValue(MatrixProperty, value);
+    }
+
     public static readonly StyledProperty<ExecutionTrigger<string>> RequestEditTextProperty =
         AvaloniaProperty.Register<TextOverlay, ExecutionTrigger<string>>(
             nameof(RequestEditText));
@@ -90,6 +100,8 @@ internal class TextOverlay : Overlay
         IsEditingProperty.Changed.Subscribe(IsEditingChanged);
         TextProperty.Changed.Subscribe(TextChanged);
         FontProperty.Changed.Subscribe(FontChanged);
+
+        AffectsOverlayRender(FontProperty, TextProperty, CursorPositionProperty, SelectionLengthProperty);
     }
 
     public TextOverlay()
@@ -108,17 +120,24 @@ internal class TextOverlay : Overlay
     public override void RenderOverlay(Canvas context, RectD canvasBounds)
     {
         if (!IsEditing) return;
-        
+
         blinker.BlinkerPosition = CursorPosition;
         blinker.FontSize = Font.Size;
         blinker.GlyphPositions = glyphPositions;
         blinker.GlyphWidths = glyphWidths;
         blinker.Offset = Position;
+        
+        int saved = context.Save();
+        
+        context.SetMatrix(context.TotalMatrix.Concat(Matrix));
 
+        blinker.BlinkerWidth = 3f / (float)ZoomScale;
         blinker.Render(context);
+        
+        context.RestoreToCount(saved);
     }
 
-    protected override void OnKeyPressed(Key key, KeyModifiers keyModifiers)
+    protected override void OnKeyPressed(Key key, KeyModifiers keyModifiers, string? keySymbol)
     {
         if (!IsEditing) return;
         if (IsShortcut(key, keyModifiers))
@@ -127,35 +146,35 @@ internal class TextOverlay : Overlay
             return;
         }
 
-        InsertChar(key, keyModifiers);
+        InsertChar(key, keySymbol);
     }
 
-    private void InsertChar(Key key, KeyModifiers keyModifiers)
+    private void InsertChar(Key key, string symbol)
     {
         if (key == Key.Enter)
         {
-            Text += Environment.NewLine;
+            InsertTextAtCursor("\n");
         }
         else if (key == Key.Space)
         {
-            Text += " ";
-            CursorPosition++;
+            InsertTextAtCursor(" ");
         }
         else
         {
-            string converted = IOperatingSystem.Current.InputKeys.GetKeyboardKey(key);
-            if (converted == null || converted.Length > 1) return;
-
-            string toAdd = keyModifiers.HasFlag(KeyModifiers.Shift) ? converted.ToUpper() : converted.ToLower();
-            char? keyChar = toAdd.FirstOrDefault();
-            if (keyChar != null)
+            if (symbol is { Length: 1 })
             {
-                if (char.IsControl(keyChar.Value)) return;
-                Text += keyChar;
-                CursorPosition++;
+                char symbolChar = symbol[0];
+                if (char.IsControl(symbolChar)) return;
+                InsertTextAtCursor(symbol);
             }
         }
     }
+    
+    private void InsertTextAtCursor(string toAdd)
+    {
+        Text = Text.Insert(CursorPosition, toAdd);
+        CursorPosition += toAdd.Length;
+    }
 
     private bool IsShortcut(Key key, KeyModifiers keyModifiers)
     {
@@ -173,24 +192,25 @@ internal class TextOverlay : Overlay
 
     private void PasteText()
     {
-        ClipboardController.GetTextFromClipboard().ContinueWith(t =>
-        {
-            Dispatcher.UIThread.Invoke(() => Text += t.Result);
-        }, TaskContinuationOptions.OnlyOnRanToCompletion);
+        ClipboardController.GetTextFromClipboard().ContinueWith(
+            t =>
+            {
+                Dispatcher.UIThread.Invoke(() => Text += t.Result);
+            }, TaskContinuationOptions.OnlyOnRanToCompletion);
     }
 
     private void DeleteChar(int direction)
     {
-        if (Text.Length > 0)
+        if (Text.Length > 0 && CursorPosition + direction >= 0 && CursorPosition + direction < Text.Length)
         {
             Text = Text.Remove(CursorPosition + direction, 1);
-            CursorPosition = Math.Clamp(CursorPosition + direction, 0, Text.Length);
+            CursorPosition = CursorPosition + direction;
         }
     }
 
     private void MoveCursorBy(int direction)
     {
-        CursorPosition = Math.Clamp(CursorPosition + direction, 0, Text.Length);
+        CursorPosition += direction;
     }
 
     private void RequestEditTextTriggered(object? sender, string e)
@@ -198,6 +218,15 @@ internal class TextOverlay : Overlay
         IsEditing = true;
     }
 
+    private void UpdateGlyphs()
+    {
+        if (Font == null) return;
+
+        RichText richText = new(Text);
+        glyphPositions = richText.GetGlyphPositions(Font);
+        glyphWidths = richText.GetGlyphWidths(Font);
+    }
+
     private static void IsVisibleChanged(AvaloniaPropertyChangedEventArgs<bool> args)
     {
         TextOverlay sender = args.Sender as TextOverlay;
@@ -247,11 +276,11 @@ internal class TextOverlay : Overlay
         sender.UpdateGlyphs();
     }
 
-    private void UpdateGlyphs()
+    private static int ClampValue(AvaloniaObject sender, int newPos)
     {
-        if (Font == null) return;
+        TextOverlay textOverlay = sender as TextOverlay;
+        if (textOverlay == null) return newPos;
 
-        glyphPositions = Font.GetGlyphPositions(Text);
-        glyphWidths = Font.GetGlyphWidths(Text);
+        return Math.Clamp(newPos, 0, textOverlay.Text.Length);
     }
 }

+ 1 - 0
src/PixiEditor/Views/Tools/ToolSettings/Settings/SizeSettingView.axaml

@@ -14,6 +14,7 @@
                      MaxSize="{Binding Max}" 
                      MinSize="{Binding Min}"
                      Decimals="{Binding DecimalPlaces}"
+                     Unit="{Binding Unit}"
                      IsEnabled="{Binding IsEnabled}" FocusNext="False"
                      Size="{Binding Value, Mode=TwoWay}"/>
 </UserControl>