Browse Source

Snapping and better preview

flabbet 1 year ago
parent
commit
ff6dfc9210

+ 4 - 4
src/PixiEditor.AvaloniaUI/Views/Nodes/ConnectionLine.cs

@@ -8,15 +8,15 @@ public class ConnectionLine : Control
 {
     private Pen pen = new() { LineCap = PenLineCap.Round };
     
-    public static readonly StyledProperty<LinearGradientBrush> ColorProperty = AvaloniaProperty.Register<ConnectionLine, LinearGradientBrush>("Color");
+    public static readonly StyledProperty<LinearGradientBrush> LineBrushProperty = AvaloniaProperty.Register<ConnectionLine, LinearGradientBrush>("LineBrush");
     public static readonly StyledProperty<double> ThicknessProperty = AvaloniaProperty.Register<ConnectionLine, double>("Thickness");
     public static readonly StyledProperty<Point> StartPointProperty = AvaloniaProperty.Register<ConnectionLine, Point>("StartPoint");
     public static readonly StyledProperty<Point> EndPointProperty = AvaloniaProperty.Register<ConnectionLine, Point>("EndPoint");
 
     public LinearGradientBrush LineBrush
     {
-        get { return GetValue(ColorProperty); }
-        set { SetValue(ColorProperty, value); }
+        get { return GetValue(LineBrushProperty); }
+        set { SetValue(LineBrushProperty, value); }
     }
 
     public double Thickness
@@ -39,7 +39,7 @@ public class ConnectionLine : Control
     
     static ConnectionLine()
     {
-        AffectsRender<ConnectionLine>(ColorProperty, ThicknessProperty, StartPointProperty, EndPointProperty);
+        AffectsRender<ConnectionLine>(LineBrushProperty, ThicknessProperty, StartPointProperty, EndPointProperty);
     }
 
     public override void Render(DrawingContext context)

+ 81 - 14
src/PixiEditor.AvaloniaUI/Views/Nodes/NodeGraphView.cs

@@ -15,6 +15,7 @@ using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.ViewModels.Nodes;
 using PixiEditor.AvaloniaUI.Views.Nodes.Properties;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 using PixiEditor.Numerics;
 using Point = Avalonia.Point;
 
@@ -157,7 +158,10 @@ internal class NodeGraphView : Zoombox.Zoombox
     private INodeHandler startConnectionNode;
     private INodeHandler endConnectionNode;
 
+    private Point startDragConnectionPoint;
     private ConnectionLine _previewConnectionLine;
+    private NodeConnectionViewModel? _hiddenConnection;
+    private Color _startingPropColor;
 
     public NodeGraphView()
     {
@@ -184,23 +188,67 @@ internal class NodeGraphView : Zoombox.Zoombox
     {
         if (isDraggingConnection)
         {
-            Point pos = e.GetPosition(this);
-            VecD currentPoint = ToZoomboxSpace(new VecD(pos.X, pos.Y));
-            if (_previewConnectionLine != null)
+            UpdateConnectionEnd(e);
+        }
+    }
+
+    private void UpdateConnectionEnd(PointerEventArgs e)
+    {
+        Point pos = e.GetPosition(this);
+        VecD currentPoint = ToZoomboxSpace(new VecD(pos.X, pos.Y));
+
+        NodeSocket? nodeSocket = e.Source as NodeSocket;
+        
+        if (nodeSocket != null)
+        {
+            Canvas canvas = nodeSocket.FindAncestorOfType<Canvas>();
+            pos = nodeSocket.ConnectPort.TranslatePoint(
+                new Point(nodeSocket.ConnectPort.Bounds.Width / 2, nodeSocket.ConnectPort.Bounds.Height / 2),
+                canvas) ?? default;
+            currentPoint = new VecD(pos.X, pos.Y);
+        }
+
+
+        if (_previewConnectionLine != null)
+        {
+            Point endPoint = new Point(currentPoint.X, currentPoint.Y);
+
+            Color gradientStopFirstColor = _startingPropColor;
+            Color gradientStopSecondColor =
+                ((SolidColorBrush)nodeSocket?.SocketBrush)?.Color ?? gradientStopFirstColor;
+
+            if (endPoint.X > startDragConnectionPoint.X)
             {
-                _previewConnectionLine.EndPoint = new Point(currentPoint.X, currentPoint.Y);
+                _previewConnectionLine.StartPoint = endPoint;
+                _previewConnectionLine.EndPoint = startDragConnectionPoint;
+                (gradientStopFirstColor, gradientStopSecondColor) =
+                    (gradientStopSecondColor, gradientStopFirstColor);
             }
+            else
+            {
+                _previewConnectionLine.StartPoint = startDragConnectionPoint;
+                _previewConnectionLine.EndPoint = endPoint;
+            }
+
+            _previewConnectionLine.LineBrush = new LinearGradientBrush()
+            {
+                GradientStops = new GradientStops()
+                {
+                    new GradientStop(gradientStopFirstColor, 0),
+                    new GradientStop(gradientStopSecondColor, 1),
+                }
+            };
         }
     }
 
     protected override void OnPointerReleased(PointerReleasedEventArgs e)
     {
         base.OnPointerReleased(e);
-        if (startConnectionProperty != null)
+        if (startConnectionProperty is { IsInput: true } && e.Source is not NodeSocket)
         {
             SocketDrop(null);
         }
-        
+
         if (isDraggingConnection)
         {
             if (_previewConnectionLine != null)
@@ -209,6 +257,7 @@ internal class NodeGraphView : Zoombox.Zoombox
             }
 
             isDraggingConnection = false;
+            _hiddenConnection = null;
         }
     }
 
@@ -229,6 +278,18 @@ internal class NodeGraphView : Zoombox.Zoombox
                 startConnectionNode = nodeSocket.Node;
                 isDraggingConnection = true;
 
+                if (nodeSocket is { IsInput: true, Property.ConnectedOutput: not null })
+                {
+                    var conn = NodeGraph.Connections.FirstOrDefault(x => x.InputProperty == nodeSocket.Property);
+                    if (conn != null)
+                    {
+                        _hiddenConnection = conn;
+                        NodeGraph.Connections.Remove(conn);
+                        NodeView view = FindNodeView(conn.OutputNode);
+                        nodeSocket = view.GetSocket(conn.OutputProperty);
+                    }
+                }
+
                 UpdatePreviewLine(nodeSocket);
             }
             else
@@ -241,6 +302,11 @@ internal class NodeGraphView : Zoombox.Zoombox
         }
     }
 
+    private NodeView FindNodeView(INodeHandler node)
+    {
+        return this.GetVisualDescendants().OfType<NodeView>().FirstOrDefault(x => x.Node == node);
+    }
+
     private void UpdatePreviewLine(NodeSocket nodeSocket)
     {
         Canvas canvas = nodeSocket.FindAncestorOfType<Canvas>();
@@ -248,23 +314,17 @@ internal class NodeGraphView : Zoombox.Zoombox
         {
             _previewConnectionLine = new ConnectionLine();
             _previewConnectionLine.Thickness = 2;
-            _previewConnectionLine.LineBrush = new LinearGradientBrush()
-            {
-                GradientStops = new GradientStops()
-                {
-                    new GradientStop(Colors.Black, 0), new GradientStop(Colors.White, 1)
-                }
-            };
 
             canvas.Children.Insert(0, _previewConnectionLine);
         }
 
         _previewConnectionLine.IsVisible = true;
+        _startingPropColor = ((SolidColorBrush)nodeSocket.SocketBrush).Color;
         _previewConnectionLine.LineBrush = new LinearGradientBrush()
         {
             GradientStops = new GradientStops()
             {
-                new GradientStop(((SolidColorBrush)nodeSocket.SocketBrush).Color, 1),
+                new GradientStop(_startingPropColor, 1),
             }
         };
 
@@ -272,6 +332,7 @@ internal class NodeGraphView : Zoombox.Zoombox
             new Point(nodeSocket.ConnectPort.Bounds.Width / 2, nodeSocket.ConnectPort.Bounds.Height / 2),
             canvas) ?? default;
         _previewConnectionLine.EndPoint = _previewConnectionLine.StartPoint;
+        startDragConnectionPoint = _previewConnectionLine.StartPoint;
     }
 
     private void Dragged(PointerEventArgs e)
@@ -321,6 +382,12 @@ internal class NodeGraphView : Zoombox.Zoombox
 
             if (startConnectionNode == endConnectionNode)
             {
+                if (_hiddenConnection != null)
+                {
+                    NodeGraph.Connections.Add(_hiddenConnection);
+                    _hiddenConnection = null;
+                }
+
                 return;
             }
         }

+ 13 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/NodeView.cs

@@ -200,6 +200,19 @@ public class NodeView : TemplatedControl
         e.Handled = true;
     }
 
+    public NodeSocket GetSocket(INodePropertyHandler property)
+    {
+        NodePropertyView propertyView = this.GetVisualDescendants().OfType<NodePropertyView>()
+            .FirstOrDefault(x => x.DataContext == property);
+
+        if (propertyView is null)
+        {
+            return default;
+        }
+
+        return property.IsInput ? propertyView.InputSocket : propertyView.OutputSocket;
+    }
+
     public Point GetSocketPoint(INodePropertyHandler property, Canvas canvas)
     {
         NodePropertyView propertyView = this.GetVisualDescendants().OfType<NodePropertyView>()

+ 6 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/NodeSocket.cs

@@ -51,6 +51,7 @@ public class NodeSocket : TemplatedControl
         ConnectPort = e.NameScope.Find<Control>("PART_ConnectPort");
         ConnectPort.PointerPressed += ConnectPortOnPointerPressed;
         ConnectPort.PointerReleased += ConnectPortOnPointerReleased;
+        ConnectPort.PointerMoved += ConnectPortOnPointerMoved;
     }
 
     private void ConnectPortOnPointerPressed(object? sender, PointerPressedEventArgs e)
@@ -58,6 +59,11 @@ public class NodeSocket : TemplatedControl
         e.Source = this;
         e.Pointer.Capture(null);
     }
+    
+    private void ConnectPortOnPointerMoved(object? sender, PointerEventArgs e)
+    {
+        e.Source = this;
+    }
 
     private void ConnectPortOnPointerReleased(object? sender, PointerEventArgs e)
     {