Переглянути джерело

Added text on path node and some overlay fixes

Krzysztof Krysiński 5 місяців тому
батько
коміт
37e8c854bf

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 26bbe22dc221c9b6a853414d3de5466c9adf1137
+Subproject commit dc3886c56d8df2596f01098622f18a859e2a82a4

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/ShapeVectorData.cs

@@ -29,6 +29,7 @@ public abstract class ShapeVectorData : ICacheable, ICloneable, IReadOnlyShapeVe
     }
     
     public bool Fill { get; set; } = true;
+
     public abstract RectD GeometryAABB { get; }
     public abstract RectD VisualAABB { get; }
     public RectD TransformedAABB => new ShapeCorners(GeometryAABB).WithMatrix(TransformationMatrix).AABBBounds;

+ 13 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/TextVectorData.cs

@@ -14,6 +14,7 @@ public class TextVectorData : ShapeVectorData, IReadOnlyTextData
     private Font font = Font.CreateDefault();
     private double? spacing = null;
     private double strokeWidth = 1;
+    private VectorPath? path;
 
     public string Text
     {
@@ -112,7 +113,18 @@ public class TextVectorData : ShapeVectorData, IReadOnlyTextData
         new ShapeCorners(GeometryAABB).WithMatrix(TransformationMatrix);
 
     public override RectD VisualAABB => GeometryAABB;
-    public VectorPath? Path { get; set; }
+
+    public VectorPath? Path
+    {
+        get => path;
+        set
+        {
+            path = value;
+            // TODO: properly calculate bounds
+            //lastBounds = richText.MeasureBounds(Font);
+        }
+    }
+
     public FontFamilyName? MissingFontFamily { get; set; }
     public string MissingFontText { get; set; }
 

+ 6 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShapeNode.cs

@@ -12,11 +12,12 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
 public class RasterizeShapeNode : RenderNode
 {
     public InputProperty<ShapeVectorData> Data { get; }
-
+    public InputProperty<bool> HighDpiRendering { get; }
 
     public RasterizeShapeNode()
     {
         Data = CreateInput<ShapeVectorData>("Points", "SHAPE", null);
+        HighDpiRendering = CreateInput<bool>("High DPI Rendering", "HIGH_DPI_RENDERING", true);
     }
 
     protected override void OnPaint(RenderContext context, DrawingSurface surface)
@@ -25,11 +26,14 @@ public class RasterizeShapeNode : RenderNode
 
         if (shape == null || !shape.IsValid())
             return;
-        
+
+        AllowHighDpiRendering = HighDpiRendering.Value;
+
         shape.RasterizeTransformed(surface.Canvas);
     }
 
     public override Node CreateCopy() => new RasterizeShapeNode();
+
     public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
     {
         return Data?.Value?.TransformedAABB;

+ 3 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/TextNode.cs

@@ -13,8 +13,7 @@ public class TextNode : ShapeNode<TextVectorData>
     public InputProperty<VecD> TextPosition { get; }
     public InputProperty<FontFamilyName> FontFamily { get; }
     public InputProperty<double> FontSize { get; }
-    public InputProperty<ShapeVectorData> OnPathData { get; }
-    
+
     private string lastText = "";
     private VecD lastPosition = new VecD();
     private FontFamilyName lastFontFamily = new FontFamilyName();
@@ -28,7 +27,6 @@ public class TextNode : ShapeNode<TextVectorData>
         TextPosition = CreateInput("Position", "POSITION", new VecD());
         FontFamily = CreateInput("FontFamily", "FONT_LABEL", new FontFamilyName());
         FontSize = CreateInput("FontSize", "FONT_SIZE_LABEL", 12d);
-        OnPathData = CreateInput<ShapeVectorData>("PathToDrawOn", "ON_PATH_DATA", null);
     }
     
     protected override TextVectorData? GetShapeData(RenderContext context)
@@ -37,9 +35,8 @@ public class TextNode : ShapeNode<TextVectorData>
         VecD position = TextPosition.Value;
         FontFamilyName fontFamily = FontFamily.Value;
         double fontSize = FontSize.Value;
-        VectorPath? path = OnPathData.Value?.ToPath();
-        
-        if (text == lastText && position == lastPosition && fontFamily.Equals(lastFontFamily) && fontSize == lastFontSize && path == lastPath)
+
+        if (text == lastText && position == lastPosition && fontFamily.Equals(lastFontFamily) && fontSize == lastFontSize)
         {
             return cachedData;
         }
@@ -48,7 +45,6 @@ public class TextNode : ShapeNode<TextVectorData>
         lastPosition = position;
         lastFontFamily = fontFamily;
         lastFontSize = fontSize;
-        lastPath = path;
 
         Font font = Font.FromFontFamily(fontFamily);
         if(font == null)
@@ -63,7 +59,6 @@ public class TextNode : ShapeNode<TextVectorData>
             Text = text,
             Position = position,
             Font = font,
-            Path = path,
         };
         
         return cachedData;

+ 51 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/TextOnPathNode.cs

@@ -0,0 +1,51 @@
+using Drawie.Backend.Core.Vector;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+
+[NodeInfo("TextOnPath")]
+public class TextOnPathNode : Node
+{
+    public InputProperty<TextVectorData> TextData { get; }
+    public InputProperty<ShapeVectorData> PathData { get; }
+
+    public OutputProperty<TextVectorData> Output { get; }
+
+    private VectorPath lastPath;
+
+    public TextOnPathNode()
+    {
+        TextData = CreateInput<TextVectorData>("Text", "TEXT_LABEL", null);
+        PathData = CreateInput<ShapeVectorData>("Path", "SHAPE_LABEL", null);
+
+        Output = CreateOutput<TextVectorData>("Output", "TEXT_LABEL", null);
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+        var textData = TextData.Value;
+        var pathData = PathData.Value;
+
+        if (textData == null || pathData == null || !textData.IsValid() || !pathData.IsValid())
+        {
+            Output.Value = null;
+            return;
+        }
+
+        var cloned = (TextVectorData)textData.Clone();
+
+        lastPath?.Dispose();
+        lastPath = pathData.ToPath();
+        lastPath.Transform(pathData.TransformationMatrix);
+
+        cloned.Path = lastPath;
+
+        Output.Value = cloned;
+    }
+
+    public override Node CreateCopy()
+    {
+        return new TextOnPathNode();
+    }
+}

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

@@ -854,5 +854,6 @@
   "USE_SRGB_PROCESSING_DESC": "Convert document using linear sRGB to sRGB for processing colors. This will affect the colors of the document.",
   "TEXT_NODE": "Text",
   "TEXT_LABEL": "Text",
-  "ON_PATH_DATA": "On path"
+  "TEXT_ON_PATH_NODE": "Text on Path",
+  "HIGH_DPI_RENDERING": "High DPI Rendering"
 }

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

@@ -62,4 +62,14 @@ internal abstract class UpdateableChangeExecutor
     public virtual void OnSettingsChanged(string name, object value) { }
     public virtual void OnColorChanged(Color color, bool primary) { }
     public virtual void OnMembersSelected(List<Guid> memberGuids) { }
+
+    protected T[] QueryLayers<T>(VecD pos) where T : ILayerHandler
+    {
+        var allLayers = document.StructureHelper.GetAllLayers();
+        var topMostWithinClick = allLayers.Where(x =>
+                x is T { IsVisibleBindable: true, TightBounds: not null } &&
+                x.TightBounds.Value.ContainsInclusive(pos))
+            .OrderByDescending(x => allLayers.IndexOf(x));
+        return topMostWithinClick.Cast<T>().ToArray();
+    }
 }

+ 44 - 8
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorTextToolExecutor.cs

@@ -3,8 +3,10 @@ using Avalonia.Threading;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Text;
+using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
 using PixiEditor.ChangeableDocument.Actions.Generated;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers.InputDevice;
@@ -28,6 +30,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
     private Matrix3X3 lastMatrix = Matrix3X3.Identity;
     private Font? cachedFont;
     private bool isListeningForValidLayer;
+    private VectorPath? onPath;
 
     public override bool BlocksOtherActions => false;
 
@@ -72,6 +75,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             toolbar.Bold = textData.Font.Bold;
             toolbar.Italic = textData.Font.Italic;
 
+            onPath = textData.Path;
             lastText = textData.Text;
             position = textData.Position;
             lastMatrix = textData.TransformationMatrix;
@@ -82,6 +86,11 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
                 Matrix3X3.Identity, toolbar.Spacing);
             lastText = "";
             position = controller.LastPrecisePosition;
+            // TODO: Implement proper putting on path editing
+            /*if (controller.LeftMousePressed)
+            {
+                TryPutOnPath(controller.LastPrecisePosition);
+            }*/
         }
         else
         {
@@ -93,11 +102,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
 
     public override void OnLeftMouseButtonDown(MouseOnCanvasEventArgs args)
     {
-        var allLayers = document.StructureHelper.GetAllLayers();
-        var topMostWithinClick = allLayers.Where(x =>
-                x is IVectorLayerHandler { IsVisibleBindable: true, TightBounds: not null } &&
-                x.TightBounds.Value.ContainsInclusive(args.PositionOnCanvas))
-            .OrderByDescending(x => allLayers.IndexOf(x));
+        var topMostWithinClick = QueryLayers<IVectorLayerHandler>(args.PositionOnCanvas);
 
         var firstLayer = topMostWithinClick.FirstOrDefault();
         args.Handled = firstLayer != null;
@@ -108,15 +113,16 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
                 args.Handled = true;
                 document.TextOverlayHandler.Hide();
             }
+
             return;
         }
 
         document.Operations.SetSelectedMember(layerHandler.Id);
         document.Operations.InvokeCustomAction(
             () =>
-        {
-            document.TextOverlayHandler.SetCursorPosition(args.PositionOnCanvas);
-        }, false);
+            {
+                document.TextOverlayHandler.SetCursorPosition(args.PositionOnCanvas);
+            }, false);
     }
 
     public void OnQuickToolSwitch()
@@ -192,6 +198,35 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
         toolbar.FillColor = color.ToColor();
     }
 
+    private void TryPutOnPath(VecD pos)
+    {
+        var topMostWithinClick = QueryLayers<IVectorLayerHandler>(pos);
+        var firstValidLayer = topMostWithinClick.FirstOrDefault(x =>
+            x.GetShapeData(document.AnimationHandler.ActiveFrameTime) is not null and not TextVectorData);
+
+        if (firstValidLayer is null)
+        {
+            return;
+        }
+
+        var shape = firstValidLayer.GetShapeData(document.AnimationHandler.ActiveFrameTime);
+
+        ShapeVectorData newShape = (ShapeVectorData)(shape as ShapeVectorData).Clone();
+
+        newShape.Fill = false;
+        newShape.StrokeWidth = 0;
+
+        onPath = newShape.ToPath();
+
+        var constructedText = ConstructTextData(lastText);
+        internals.ActionAccumulator.AddFinishedActions(
+            new SetShapeGeometry_Action(selectedMember.Id, constructedText),
+            new EndSetShapeGeometry_Action(),
+            new SetLowDpiRendering_Action(selectedMember.Id, toolbar.ForceLowDpiRendering),
+            new SetShapeGeometry_Action(firstValidLayer.Id, newShape),
+            new EndSetShapeGeometry_Action());
+    }
+
     private TextVectorData ConstructTextData(string text)
     {
         if (cachedFont == null || cachedFont.Family.Name != toolbar.FontFamily.Name)
@@ -220,6 +255,7 @@ internal class VectorTextToolExecutor : UpdateableChangeExecutor, ITextOverlayEv
             Font = cachedFont,
             Spacing = toolbar.Spacing,
             AntiAlias = toolbar.AntiAliasing,
+            Path = onPath,
             // TODO: MaxWidth = toolbar.MaxWidth
             // TODO: Path
         };

+ 9 - 0
src/PixiEditor/ViewModels/Document/Nodes/Shapes/TextOnPathNodeViewModel.cs

@@ -0,0 +1,9 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Shapes;
+
+[NodeViewModel("TEXT_ON_PATH_NODE", "SHAPE", "\uE998")]
+internal class TextOnPathNodeViewModel : NodeViewModel<TextOnPathNode>
+{
+}

+ 20 - 0
src/PixiEditor/Views/Overlays/Overlay.cs

@@ -123,6 +123,18 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
         PointerMovedOverlay?.Invoke(args);
     }
 
+    public void FocusChanged(bool focused)
+    {
+        if (focused)
+        {
+            OnOverlayGotFocus();
+        }
+        else
+        {
+            OnOverlayLostFocus();
+        }
+    }
+
     public void PressPointer(OverlayPointerArgs args)
     {
         InvokeHandleEvent(HandleEventType.PointerPressedOverlay, args);
@@ -306,6 +318,14 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
     {
     }
 
+    protected virtual void OnOverlayLostFocus()
+    {
+    }
+
+    protected virtual void OnOverlayGotFocus()
+    {
+    }
+
     private static void OnZoomScaleChanged(AvaloniaPropertyChangedEventArgs<double> e)
     {
         if (e.Sender is Overlay overlay)

+ 14 - 0
src/PixiEditor/Views/Overlays/TextOverlay/TextOverlay.cs

@@ -1,5 +1,6 @@
 using Avalonia;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.Threading;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
@@ -215,6 +216,19 @@ internal class TextOverlay : Overlay
         Refresh();
     }
 
+    protected override void OnOverlayLostFocus()
+    {
+        ShortcutController.UnblockShortcutExecution(nameof(TextOverlay));
+    }
+
+    protected override void OnOverlayGotFocus()
+    {
+        if (IsEditing)
+        {
+            ShortcutController.BlockShortcutExecution(nameof(TextOverlay));
+        }
+    }
+
     private void RenderCaret(Canvas context)
     {
         caret.CaretPosition = CursorPosition;

+ 26 - 0
src/PixiEditor/Views/Rendering/Scene.cs

@@ -502,6 +502,32 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         Focus();
     }
 
+    protected override void OnGotFocus(GotFocusEventArgs e)
+    {
+        base.OnGotFocus(e);
+        if (AllOverlays != null)
+        {
+            foreach (Overlay overlay in AllOverlays)
+            {
+                if (!overlay.IsVisible) continue;
+                overlay.FocusChanged(true);
+            }
+        }
+    }
+
+    protected override void OnLostFocus(RoutedEventArgs e)
+    {
+        base.OnLostFocus(e);
+        if (AllOverlays != null)
+        {
+            foreach (Overlay overlay in AllOverlays)
+            {
+                if (!overlay.IsVisible) continue;
+                overlay.FocusChanged(false);
+            }
+        }
+    }
+
     private VecD ToCanvasSpace(Point scenePosition)
     {
         Matrix transform = CalculateTransformMatrix();