Browse Source

Merge branch 'refs/heads/avalonia-rewrite' into structure-member-outputs

CPKreuz 1 year ago
parent
commit
5cbfe43194

+ 13 - 3
src/PixiEditor.AvaloniaUI/Helpers/StructureHelpers.cs

@@ -6,6 +6,7 @@ namespace PixiEditor.AvaloniaUI.Helpers;
 public static class StructureHelpers
 {
     public const int PreviewSize = 48;
+
     /// <summary>
     /// Calculates the size of a scaled-down preview for a given size of layer tight bounds.
     /// </summary>
@@ -13,8 +14,17 @@ public static class StructureHelpers
     {
         double proportions = tightBoundsSize.Y / (double)tightBoundsSize.X;
         const int prSize = PreviewSize;
-        return proportions > 1 ?
-            new VecI(Math.Max((int)Math.Round(prSize / proportions), 1), prSize) :
-            new VecI(prSize, Math.Max((int)Math.Round(prSize * proportions), 1));
+        return proportions > 1
+            ? new VecI(Math.Max((int)Math.Round(prSize / proportions), 1), prSize)
+            : new VecI(prSize, Math.Max((int)Math.Round(prSize * proportions), 1));
+    }
+
+    public static VecI CalculatePreviewSize(VecI tightBoundsSize, int previewSize)
+    {
+        double proportions = tightBoundsSize.Y / (double)tightBoundsSize.X;
+        int prSize = previewSize;
+        return proportions > 1
+            ? new VecI(Math.Max((int)Math.Round(prSize / proportions), 1), prSize)
+            : new VecI(prSize, Math.Max((int)Math.Round(prSize * proportions), 1));
     }
 }

+ 1 - 1
src/PixiEditor.AvaloniaUI/Models/Rendering/MemberPreviewUpdater.cs

@@ -739,7 +739,7 @@ internal class MemberPreviewUpdater
             if (nodeVm.ResultPreview == null)
             {
                 nodeVm.ResultPreview =
-                    new Surface(StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size));
+                    new Surface(StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size, 150));
             }
 
             float scalingX = (float)nodeVm.ResultPreview.Size.X / node.CachedResult.Size.X;

+ 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)

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

@@ -6,6 +6,8 @@ using Avalonia.Controls;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Primitives;
 using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.VisualTree;
 using CommunityToolkit.Mvvm.Input;
 using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Models.Handlers;
@@ -13,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;
 
@@ -48,19 +51,24 @@ internal class NodeGraphView : Zoombox.Zoombox
         AvaloniaProperty.Register<NodeGraphView, ICommand>(
             nameof(EndChangeNodePosCommand));
 
-    public static readonly StyledProperty<string> SearchQueryProperty = AvaloniaProperty.Register<NodeGraphView, string>(
-        nameof(SearchQuery));
+    public static readonly StyledProperty<string> SearchQueryProperty =
+        AvaloniaProperty.Register<NodeGraphView, string>(
+            nameof(SearchQuery));
 
-    public static readonly StyledProperty<ObservableCollection<Type>> AllNodeTypesProperty = AvaloniaProperty.Register<NodeGraphView, ObservableCollection<Type>>(
-        nameof(AllNodeTypes));
+    public static readonly StyledProperty<ObservableCollection<Type>> AllNodeTypesProperty =
+        AvaloniaProperty.Register<NodeGraphView, ObservableCollection<Type>>(
+            nameof(AllNodeTypes));
 
-    public static readonly StyledProperty<ICommand> SocketDropCommandProperty = AvaloniaProperty.Register<NodeGraphView, ICommand>(
-        nameof(SocketDropCommand));
+    public static readonly StyledProperty<ICommand> SocketDropCommandProperty =
+        AvaloniaProperty.Register<NodeGraphView, ICommand>(
+            nameof(SocketDropCommand));
 
-    public static readonly StyledProperty<ICommand> CreateNodeCommandProperty = AvaloniaProperty.Register<NodeGraphView, ICommand>("CreateNodeCommand");
+    public static readonly StyledProperty<ICommand> CreateNodeCommandProperty =
+        AvaloniaProperty.Register<NodeGraphView, ICommand>("CreateNodeCommand");
 
-    public static readonly StyledProperty<ICommand> ConnectPropertiesCommandProperty = AvaloniaProperty.Register<NodeGraphView, ICommand>(
-        "ConnectPropertiesCommand");
+    public static readonly StyledProperty<ICommand> ConnectPropertiesCommandProperty =
+        AvaloniaProperty.Register<NodeGraphView, ICommand>(
+            "ConnectPropertiesCommand");
 
     public ICommand ConnectPropertiesCommand
     {
@@ -150,6 +158,11 @@ 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()
     {
         SelectNodeCommand = new RelayCommand<PointerPressedEventArgs>(SelectNode);
@@ -171,13 +184,81 @@ internal class NodeGraphView : Zoombox.Zoombox
         }
     }
 
+    protected override void OnPointerMoved(PointerEventArgs e)
+    {
+        if (isDraggingConnection)
+        {
+            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.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)
+            {
+                _previewConnectionLine.IsVisible = false;
+            }
+
+            isDraggingConnection = false;
+            _hiddenConnection = null;
+        }
     }
 
     private IEnumerable<Type> GatherAssemblyTypes<T>()
@@ -196,6 +277,20 @@ internal class NodeGraphView : Zoombox.Zoombox
                 startConnectionProperty = nodeSocket.Property;
                 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
             {
@@ -207,12 +302,46 @@ 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>();
+        if (_previewConnectionLine == null)
+        {
+            _previewConnectionLine = new ConnectionLine();
+            _previewConnectionLine.Thickness = 2;
+
+            canvas.Children.Insert(0, _previewConnectionLine);
+        }
+
+        _previewConnectionLine.IsVisible = true;
+        _startingPropColor = ((SolidColorBrush)nodeSocket.SocketBrush).Color;
+        _previewConnectionLine.LineBrush = new LinearGradientBrush()
+        {
+            GradientStops = new GradientStops()
+            {
+                new GradientStop(_startingPropColor, 1),
+            }
+        };
+
+        _previewConnectionLine.StartPoint = nodeSocket.ConnectPort.TranslatePoint(
+            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)
     {
         if (isDraggingNodes)
         {
             Point pos = e.GetPosition(this);
             VecD currentPoint = ToZoomboxSpace(new VecD(pos.X, pos.Y));
+
             VecD delta = currentPoint - clickPointOffset;
             foreach (var node in SelectedNodes)
             {
@@ -232,11 +361,11 @@ internal class NodeGraphView : Zoombox.Zoombox
 
     private void SocketDrop(NodeSocket socket)
     {
-        if(startConnectionProperty == null)
+        if (startConnectionProperty == null)
         {
             return;
         }
-        
+
         (INodePropertyHandler, INodePropertyHandler) connection = (startConnectionProperty, null);
         if (socket != null)
         {
@@ -253,15 +382,21 @@ internal class NodeGraphView : Zoombox.Zoombox
 
             if (startConnectionNode == endConnectionNode)
             {
+                if (_hiddenConnection != null)
+                {
+                    NodeGraph.Connections.Add(_hiddenConnection);
+                    _hiddenConnection = null;
+                }
+
                 return;
             }
         }
 
-        if(ConnectPropertiesCommand != null && ConnectPropertiesCommand.CanExecute(connection))
+        if (ConnectPropertiesCommand != null && ConnectPropertiesCommand.CanExecute(connection))
         {
             ConnectPropertiesCommand.Execute(connection);
         }
-        
+
         startConnectionProperty = null;
         endConnectionProperty = null;
         startConnectionNode = null;

+ 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)
     {