瀏覽代碼

Fixed moving multiple nodes

flabbet 9 月之前
父節點
當前提交
65e9efd9dc

+ 53 - 22
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodePosition_UpdateableChange.cs

@@ -6,16 +6,19 @@ namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
 
 internal class NodePosition_UpdateableChange : UpdateableChange
 {
-    public Guid NodeId { get; }
-    public VecD NewPosition { get; private set; } 
-    
-    private VecD originalPosition;
+    public Guid[] NodeIds { get; }
+    public VecD NewPosition { get; private set; }
+
+    private Dictionary<Guid, VecD> originalPositions;
     
+    private VecD startPosition;
+
     [GenerateUpdateableChangeActions]
-    public NodePosition_UpdateableChange(Guid nodeId, VecD newPosition)
+    public NodePosition_UpdateableChange(IEnumerable<Guid> nodeIds, VecD newPosition)
     {
-        NodeId = nodeId;
+        NodeIds = nodeIds.ToArray();
         NewPosition = newPosition;
+        startPosition = newPosition;
     }
 
     [UpdateChangeMethod]
@@ -23,43 +26,71 @@ internal class NodePosition_UpdateableChange : UpdateableChange
     {
         NewPosition = newPosition;
     }
-    
+
     public override bool InitializeAndValidate(Document target)
     {
-        var node = target.FindNode<Node>(NodeId);
-        if (node == null)
+        originalPositions = new Dictionary<Guid, VecD>();
+        foreach (var nodeId in NodeIds)
         {
-            return false;
+            var node = target.FindNode<Node>(nodeId);
+            if (node == null)
+            {
+                return false;
+            }
+            
+            originalPositions.Add(nodeId, node.Position);
         }
 
-        originalPosition = node.Position;
         return true;
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
     {
-        var node = target.FindNode<Node>(NodeId);
-        node.Position = NewPosition;
-        return new NodePosition_ChangeInfo(NodeId, NewPosition);
+        List<IChangeInfo> changes = new();
+        VecD delta = NewPosition - startPosition;
+        foreach (var nodeId in NodeIds)
+        {
+            var node = target.FindNode<Node>(nodeId);
+            node.Position = originalPositions[nodeId] + delta;
+            changes.Add(new NodePosition_ChangeInfo(nodeId, node.Position));
+        }
+        
+        return changes;
     }
 
-    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
+        out bool ignoreInUndo)
     {
         ignoreInUndo = false;
-        var node = target.FindNode<Node>(NodeId);
-        node.Position = NewPosition;
-        return new NodePosition_ChangeInfo(NodeId, NewPosition);
+      
+        VecD delta = NewPosition - startPosition;
+        List<IChangeInfo> changes = new();
+        
+        foreach (var nodeId in NodeIds)
+        {
+            var node = target.FindNode<Node>(nodeId);
+            node.Position = originalPositions[nodeId] + delta;
+            changes.Add(new NodePosition_ChangeInfo(nodeId, node.Position));
+        }
+        
+        return changes;
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
-        var node = target.FindNode<Node>(NodeId);
-        node.Position = originalPosition;
-        return new NodePosition_ChangeInfo(NodeId, originalPosition);
+        List<IChangeInfo> changes = new();
+        foreach (var nodeId in NodeIds)
+        {
+            var node = target.FindNode<Node>(nodeId);
+            node.Position = originalPositions[nodeId];
+            changes.Add(new NodePosition_ChangeInfo(nodeId, node.Position));
+        }
+        
+        return changes;
     }
 
     public override bool IsMergeableWith(Change other)
     {
-        return other is NodePosition_UpdateableChange change && change.NodeId == NodeId;
+        return other is NodePosition_UpdateableChange change && change.NodeIds.SequenceEqual(NodeIds);
     }
 }

+ 1 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -378,7 +378,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
             Guid guid = Guid.NewGuid();
             mappedNodeIds.Add(id, guid);
             acc.AddActions(new CreateNodeFromName_Action(serializedNode.UniqueNodeName, guid));
-            acc.AddFinishedActions(new NodePosition_Action(guid, serializedNode.Position.ToVecD()),
+            acc.AddFinishedActions(new NodePosition_Action([guid], serializedNode.Position.ToVecD()),
                 new EndNodePosition_Action());
 
             if (serializedNode.InputValues != null)

+ 6 - 5
src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs

@@ -209,9 +209,10 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
         return new Queue<INodeHandler>(finalQueue);
     }
 
-    public void SetNodePosition(INodeHandler node, VecD newPos)
+    public void SetNodePositions(List<INodeHandler> node, VecD startPos)
     {
-        Internals.ActionAccumulator.AddActions(new NodePosition_Action(node.Id, newPos));
+        Guid[] nodeIds = node.Select(x => x.Id).ToArray();
+        Internals.ActionAccumulator.AddActions(new NodePosition_Action(nodeIds, startPos));
     }
 
     public void UpdatePropertyValue(INodeHandler node, string property, object? value)
@@ -240,9 +241,9 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
 
             if (pos != default)
             {
-                changes.Add(new NodePosition_Action(startId, pos));
+                changes.Add(new NodePosition_Action([startId], pos));
                 changes.Add(new EndNodePosition_Action());
-                changes.Add(new NodePosition_Action(endId, new VecD(pos.X + 400, pos.Y)));
+                changes.Add(new NodePosition_Action([endId], new VecD(pos.X + 400, pos.Y)));
                 changes.Add(new EndNodePosition_Action());
             }
         }
@@ -253,7 +254,7 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
 
             if (pos != default)
             {
-                changes.Add(new NodePosition_Action(nodeId, pos));
+                changes.Add(new NodePosition_Action([nodeId], pos));
                 changes.Add(new EndNodePosition_Action());
             }
         }

+ 1 - 1
src/PixiEditor/ViewModels/Nodes/NodeViewModel.cs

@@ -93,7 +93,7 @@ internal abstract class NodeViewModel : ObservableObject, INodeHandler
             if (!Document.BlockingUpdateableChangeActive)
             {
                 Internals.ActionAccumulator.AddFinishedActions(
-                    new NodePosition_Action(Id, value),
+                    new NodePosition_Action([Id], value),
                     new EndNodePosition_Action());
             }
         }

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/NodeGraphManagerViewModel.cs

@@ -67,9 +67,9 @@ internal class NodeGraphManagerViewModel : SubViewModel<ViewModelMain>
     }
 
     [Command.Internal("PixiEditor.NodeGraph.ChangeNodePos")]
-    public void ChangeNodePos((INodeHandler node, VecD newPos) args)
+    public void ChangeNodePos((List<INodeHandler> nodes, VecD newPos) args)
     {
-        Owner.DocumentManagerSubViewModel.ActiveDocument?.NodeGraph.SetNodePosition(args.node, args.newPos);
+        Owner.DocumentManagerSubViewModel.ActiveDocument?.NodeGraph.SetNodePositions(args.nodes, args.newPos);
     }
 
     [Command.Internal("PixiEditor.NodeGraph.UpdateValue", AnalyticsTrack = true)]

+ 1 - 7
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -244,6 +244,7 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
         const string ConfirmationDialogTitle = "UNSAVED_CHANGES";
         const string ConfirmationDialogMessage = "DOCUMENT_MODIFIED_SAVE";
 
+
         ConfirmationType result = ConfirmationType.No;
         if (!document.AllChangesSaved)
         {
@@ -268,13 +269,6 @@ internal partial class ViewModelMain : ViewModelBase, ICommandsHandler
                     WindowSubViewModel.MakeDocumentViewportActive(null);
             }
 
-            // TODO: this thing should actually dispose the document to free up ram
-            // We need the UI to be able to handle disposed documents
-            // Like, the viewports should show nothing, the commands shouldn't work, etc. At least nothing should crash or behave unexpectedly
-            // Mostly we only care about this because avalondock doesn't remove the UI elements of closed viewports (at least not right away)
-            // So they remain alive and keep "showing" the now disposed DocumentViewModel
-            // And since they reference the DocumentViewModel it doesn't get collected by GC
-
             WindowSubViewModel.CloseViewportsForDocument(document);
             document.Dispose();
 

+ 16 - 20
src/PixiEditor/Views/Nodes/NodeGraphView.cs

@@ -74,8 +74,9 @@ internal class NodeGraphView : Zoombox.Zoombox
         AvaloniaProperty.Register<NodeGraphView, ICommand>(
             "ConnectPropertiesCommand");
 
-    public static readonly StyledProperty<ICommand> CreateNodeFromContextCommandProperty = AvaloniaProperty.Register<NodeGraphView, ICommand>(
-        "CreateNodeFromContextCommand");
+    public static readonly StyledProperty<ICommand> CreateNodeFromContextCommandProperty =
+        AvaloniaProperty.Register<NodeGraphView, ICommand>(
+            "CreateNodeFromContextCommand");
 
     public ICommand CreateNodeFromContextCommand
     {
@@ -94,7 +95,7 @@ internal class NodeGraphView : Zoombox.Zoombox
         get => GetValue(AllNodeTypesProperty);
         set => SetValue(AllNodeTypesProperty, value);
     }
-    
+
     public ObservableCollection<NodeTypeInfo> AllNodeTypeInfos
     {
         get => GetValue(AllNodeTypeInfosProperty);
@@ -188,7 +189,9 @@ internal class NodeGraphView : Zoombox.Zoombox
     private NodeConnectionViewModel? _hiddenConnection;
     private Color _startingPropColor;
     private VecD _lastMouseClickPos;
-    public static readonly StyledProperty<int> ActiveFrameProperty = AvaloniaProperty.Register<NodeGraphView, int>("ActiveFrame");
+
+    public static readonly StyledProperty<int> ActiveFrameProperty =
+        AvaloniaProperty.Register<NodeGraphView, int>("ActiveFrame");
 
     public NodeGraphView()
     {
@@ -202,7 +205,7 @@ internal class NodeGraphView : Zoombox.Zoombox
         AllNodeTypes = new ObservableCollection<Type>(GatherAssemblyTypes<NodeViewModel>());
         AllNodeTypeInfos = new ObservableCollection<NodeTypeInfo>(AllNodeTypes.Select(x => new NodeTypeInfo(x)));
     }
-    
+
     private void CreateNodeType(NodeTypeInfo nodeType)
     {
         var type = nodeType.NodeType;
@@ -221,7 +224,7 @@ internal class NodeGraphView : Zoombox.Zoombox
         {
             ClearSelection();
         }
-        
+
         Point pos = e.GetPosition(this);
         _lastMouseClickPos = ToZoomboxSpace(new VecD(pos.X, pos.Y));
     }
@@ -240,7 +243,7 @@ internal class NodeGraphView : Zoombox.Zoombox
         VecD currentPoint = ToZoomboxSpace(new VecD(pos.X, pos.Y));
 
         NodeSocket? nodeSocket = e.Source as NodeSocket;
-        
+
         if (nodeSocket != null)
         {
             Canvas canvas = nodeSocket.FindAncestorOfType<Canvas>();
@@ -276,8 +279,7 @@ internal class NodeGraphView : Zoombox.Zoombox
             {
                 GradientStops = new GradientStops()
                 {
-                    new GradientStop(gradientStopFirstColor, 0),
-                    new GradientStop(gradientStopSecondColor, 1),
+                    new GradientStop(gradientStopFirstColor, 0), new GradientStop(gradientStopSecondColor, 1),
                 }
             };
         }
@@ -299,8 +301,8 @@ internal class NodeGraphView : Zoombox.Zoombox
         {
             return gradientBrush.GradientStops.FirstOrDefault()?.Color;
         }
-        
-        return null; 
+
+        return null;
     }
 
     protected override void OnPointerReleased(PointerReleasedEventArgs e)
@@ -380,13 +382,10 @@ internal class NodeGraphView : Zoombox.Zoombox
         }
 
         _previewConnectionLine.IsVisible = true;
-        _startingPropColor = GetSocketColor(nodeSocket) ?? Colors.White; 
+        _startingPropColor = GetSocketColor(nodeSocket) ?? Colors.White;
         _previewConnectionLine.LineBrush = new LinearGradientBrush()
         {
-            GradientStops = new GradientStops()
-            {
-                new GradientStop(_startingPropColor, 1),
-            }
+            GradientStops = new GradientStops() { new GradientStop(_startingPropColor, 1), }
         };
 
         _previewConnectionLine.StartPoint = nodeSocket.ConnectPort.TranslatePoint(
@@ -404,10 +403,7 @@ internal class NodeGraphView : Zoombox.Zoombox
             VecD currentPoint = ToZoomboxSpace(new VecD(pos.X, pos.Y));
 
             VecD delta = currentPoint - clickPointOffset;
-            foreach (var node in SelectedNodes)
-            {
-                ChangeNodePosCommand?.Execute((node, initialNodePositions[SelectedNodes.IndexOf(node)] + delta));
-            }
+            ChangeNodePosCommand?.Execute((SelectedNodes, initialNodePositions[0] + delta));
         }
     }