Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Alt + Shift + Drag to copy & move #1544

Open
damonamyx opened this issue Sep 17, 2023 · 1 comment
Open

Suggestion: Alt + Shift + Drag to copy & move #1544

damonamyx opened this issue Sep 17, 2023 · 1 comment

Comments

@damonamyx
Copy link

Alt + Shift + Drag to copy would fit in smoothly with Ctrl + Click/Drag to select and Shift + Drag to move, extending the functionality of one of the core command series at a very low implementation cost.

That is all

@IntendedConsequence
Copy link

I hacked in something like this to my local comfyui install. No time to do a proper fork&PR atm, so I'm posting this patch if anyone finds it useful.
Changes:

  • Alt+Drag clones entire selection, 1 node or many.
  • Alt+Shift+Drag same as above, but like Ctrl+Shift+V, clones with incoming branches

Limitations:

  • Alt+Shift+Drag for single nodes only works if the node was already selected.
  • The currently selected node detection after cloned nodes are added is jank, just finds it from mouse coords, no idea if it works as it should. The intent is to have a cloned node of the one under cursor designated as the "draggee", probably should just search it in the paste stage and match by id.

Description:
Uses the clipboard copy-paste logic, skipping the JSON encode/decode and storing to local storage.

Note:
The patch was exported ignoring whitespace changes. The original litegraph.core.js file has inconsistent indentation (mixes tabs and spaces) and lots of trailing spaces. My editor trims trailing spaces on save. Should work, but who knows. I don't write Javascript much.

The patch:

diff --git a/web/lib/litegraph.core.js b/web/lib/litegraph.core.js
index e906590..ff7521b 100644
--- a/web/lib/litegraph.core.js
+++ b/web/lib/litegraph.core.js
@@ -5898,7 +5898,24 @@ LGraphNode.prototype.executeAction = function(action)
             // clone node ALT dragging
             if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only)
             {
-                if (cloned = node.clone()){
+                // nodes already selected
+                if (Object.keys(this.selected_nodes).length) {
+                    var clipboard_info = this.prepareDataForClipboard()
+
+                    var isConnectUnselected = e.shiftKey
+
+                    this.graph.beforeChange();
+                    //create nodes
+                    this.pasteFromClipboardWithData(clipboard_info, isConnectUnselected, false);
+                    skip_action = true;
+
+                    // drag newly duplicated node under the cursor
+                    node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 );
+                    this.node_dragged = node;
+
+                    this.graph.afterChange();
+                }
+                else if (cloned = node.clone()){
                     cloned.pos[0] += 5;
                     cloned.pos[1] += 5;
                     this.graph.add(cloned,false,{doCalcSize: false});
@@ -7110,7 +7127,7 @@ LGraphNode.prototype.executeAction = function(action)
         }
     };
 
-    LGraphCanvas.prototype.copyToClipboard = function() {
+    LGraphCanvas.prototype.prepareDataForClipboard = function() {
         var clipboard_info = {
             nodes: [],
             links: []
@@ -7129,8 +7146,7 @@ LGraphNode.prototype.executeAction = function(action)
         for (var i = 0; i < selected_nodes_array.length; ++i) {
             var node = selected_nodes_array[i];
             var cloned = node.clone();
-            if(!cloned)
-            {
+            if (!cloned) {
                 console.warn("node type not found: " + node.type);
                 continue;
             }
@@ -7153,7 +7169,7 @@ LGraphNode.prototype.executeAction = function(action)
                     }
                     clipboard_info.links.push([
                         target_node._relative_id,
-                        link_info.origin_slot, //j,
+                        link_info.origin_slot,
                         node._relative_id,
                         link_info.target_slot,
                         target_node.id
@@ -7161,29 +7177,28 @@ LGraphNode.prototype.executeAction = function(action)
                 }
             }
         }
+        return clipboard_info;
+    }
+
+    LGraphCanvas.prototype.copyToClipboard = function() {
+        var clipboard_info = this.prepareDataForClipboard();
         localStorage.setItem(
             "litegrapheditor_clipboard",
             JSON.stringify(clipboard_info)
         );
     };
 
-    LGraphCanvas.prototype.pasteFromClipboard = function(isConnectUnselected = false) {
-        // if ctrl + shift + v is off, return when isConnectUnselected is true (shift is pressed) to maintain old behavior
-        if (!LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs && isConnectUnselected) {
-            return;
-        }
-        var data = localStorage.getItem("litegrapheditor_clipboard");
-        if (!data) {
-            return;
-        }
-
-		this.graph.beforeChange();
-
-        //create nodes
-        var clipboard_info = JSON.parse(data);
+    LGraphCanvas.prototype.pasteFromClipboardWithData = function(clipboard_info, isConnectUnselected = false, calculatePasteOffset = true) {
         // calculate top-left node, could work without this processing but using diff with last node pos :: clipboard_info.nodes[clipboard_info.nodes.length-1].pos
         var posMin = false;
         var posMinIndexes = false;
+
+        var constantPasteOffset = 5;
+        var pasteOffsetX = constantPasteOffset;
+        var pasteOffsetY = constantPasteOffset;
+
+        if (calculatePasteOffset)
+        {
             for (var i = 0; i < clipboard_info.nodes.length; ++i) {
                 if (posMin){
                     if(posMin[0]>clipboard_info.nodes[i].pos[0]){
@@ -7200,6 +7215,11 @@ LGraphNode.prototype.executeAction = function(action)
                     posMinIndexes = [i, i];
                 }
             }
+
+            pasteOffsetX = this.graph_mouse[0] - posMin[0];
+            pasteOffsetY = this.graph_mouse[1] - posMin[1];
+        }
+
         var nodes = [];
         for (var i = 0; i < clipboard_info.nodes.length; ++i) {
             var node_data = clipboard_info.nodes[i];
@@ -7208,8 +7228,8 @@ LGraphNode.prototype.executeAction = function(action)
                 node.configure(node_data);
 
 				//paste in last known mouse position
-                node.pos[0] += this.graph_mouse[0] - posMin[0]; //+= 5;
-                node.pos[1] += this.graph_mouse[1] - posMin[1]; //+= 5;
+                node.pos[0] += pasteOffsetX; //+= 5;
+                node.pos[1] += pasteOffsetY; //+= 5;
 
                 this.graph.add(node,{doProcessChange:false});
 
@@ -7238,6 +7258,24 @@ LGraphNode.prototype.executeAction = function(action)
         }
 
         this.selectNodes(nodes);
+    }
+    LGraphCanvas.prototype.pasteFromClipboard = function(isConnectUnselected = false) {
+        // if ctrl + shift + v is off, return when isConnectUnselected is true (shift is pressed) to maintain old behavior
+        if (!LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs && isConnectUnselected) {
+            return;
+        }
+        var data = localStorage.getItem("litegrapheditor_clipboard");
+
+        if (!data) {
+            return;
+        }
+
+		this.graph.beforeChange();
+
+        //create nodes
+        var clipboard_info = JSON.parse(data);
+        this.pasteFromClipboardWithData(clipboard_info, isConnectUnselected);
+
 
 		this.graph.afterChange();
     };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants