From 1d118d95acc5765d8e322f29beba764e8d46b887 Mon Sep 17 00:00:00 2001 From: atlasan Date: Fri, 15 Oct 2021 16:37:25 +0200 Subject: [PATCH] MultiActions for context menu - right click node menu actions will be applied to all selected nodes (mode, resize, collapse, colors, shapes, clone, remove) - can add to selection with both cntrl and shift, and dragging new rects with cntr+shift - TODO apply to alt-drag clone, properties(?), .. --- src/litegraph.js | 360 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 243 insertions(+), 117 deletions(-) diff --git a/src/litegraph.js b/src/litegraph.js index 3bcb1661bb..e14487eec8 100644 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -1626,13 +1626,19 @@ */ LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { nodes_list = nodes_list || this._nodes; + var nRet = null; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; if (n.isPointInside(x, y, margin)) { - return n; + // check for lesser interest nodes (TODO check for overlapping, use the top) + /*if (typeof n == "LGraphGroup"){ + nRet = n; + }else{*/ + return n; + /*}*/ } } - return null; + return nRet; }; /** @@ -6040,49 +6046,51 @@ LGraphNode.prototype.executeAction = function(action) } } //clicked outside of nodes else { - //search for link connector - if(!this.read_only) - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || - e.canvasX < center[0] - 4 || - e.canvasX > center[0] + 4 || - e.canvasY < center[1] - 4 || - e.canvasY > center[1] + 4 - ) { - continue; + if (!skip_action){ + //search for link connector + if(!this.read_only) { + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || + e.canvasX < center[0] - 4 || + e.canvasX > center[0] + 4 || + e.canvasY < center[1] - 4 || + e.canvasY > center[1] + 4 + ) { + continue; + } + //link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; //clear tooltip + break; } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; } - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - e.preventDefault(); - e.stopPropagation(); - - } + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + e.preventDefault(); + e.stopPropagation(); + } - clicking_canvas_bg = true; + clicking_canvas_bg = true; + } } if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { @@ -6136,7 +6144,7 @@ LGraphNode.prototype.executeAction = function(action) var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); var node_bounding = node.getBounding(); - // etimate a position: this is a bad semi-bad-working mess .. + // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2])// + node_bounding[0]/this.canvas.width*150 ,e.canvasY-80// + node_bounding[0]/this.canvas.width*66 // vertical "derive" ]; @@ -6156,9 +6164,22 @@ LGraphNode.prototype.executeAction = function(action) } } else if (e.which == 3 || this.pointer_is_double) { + //right button - if(!this.read_only) - this.processContextMenu(node, e); + if(!this.read_only){ + if(Object.keys(this.selected_nodes).length + && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) + ){ + // is multiselected or using shift to include the now node + if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present + }else{ + // update selection + this.selectNodes([node]); + } + // show menu on this node + this.processContextMenu(node, e); + } + } //TODO @@ -6529,11 +6550,17 @@ LGraphNode.prototype.executeAction = function(action) } this.selected_group_resizing = false; + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes + ); + if (this.dragging_rectangle) { if (this.graph) { var nodes = this.graph._nodes; var node_bounding = new Float32Array(4); - this.deselectAllNodes(); + //compute bounding and flip if left to right var w = Math.abs(this.dragging_rectangle[2]); var h = Math.abs(this.dragging_rectangle[3]); @@ -6550,24 +6577,31 @@ LGraphNode.prototype.executeAction = function(action) this.dragging_rectangle[2] = w; this.dragging_rectangle[3] = h; - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var node = nodes[i]; - node.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(node); - } - if (to_select.length) { - this.selectNodes(to_select); - } + // test dragging rect size, if minimun simulate a click + if (!node || (w > 10 && h > 10 )){ + //test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var nodeX = nodes[i]; + nodeX.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding + ) + ) { + continue; + } //out of the visible area + to_select.push(nodeX); + } + if (to_select.length) { + this.selectNodes(to_select,e.shiftKey); // add to selection with shift + } + }else{ + // will select of update selection + this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey + } + } this.dragging_rectangle = null; } else if (this.connecting_node) { @@ -6578,12 +6612,6 @@ LGraphNode.prototype.executeAction = function(action) var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - //node below mouse if (node) { @@ -7228,7 +7256,7 @@ LGraphNode.prototype.executeAction = function(action) }; LGraphCanvas.prototype.processNodeSelected = function(node, e) { - this.selectNode(node, e && e.shiftKey); + this.selectNode(node, e && (e.shiftKey||e.ctrlKey)); if (this.onNodeSelected) { this.onNodeSelected(node); } @@ -7255,12 +7283,13 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) { - if (!add_to_current_selection) { + if (!add_to_current_selection) { this.deselectAllNodes(); } nodes = nodes || this.graph._nodes; - for (var i = 0; i < nodes.length; ++i) { + if (typeof nodes == "string") nodes = [nodes]; + for (var i in nodes) { var node = nodes[i]; if (node.is_selected) { continue; @@ -10546,13 +10575,26 @@ LGraphNode.prototype.executeAction = function(action) return e.innerHTML; }; - LGraphCanvas.onResizeNode = function(value, options, e, menu, node) { + LGraphCanvas.onMenuResizeNode = function(value, options, e, menu, node) { if (!node) { return; } - node.size = node.computeSize(); - if (node.onResize) - node.onResize(node.size); + + var fApplyMultiNode = function(node){ + node.size = node.computeSize(); + if (node.onResize) + node.onResize(node.size); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + node.setDirtyCanvas(true, true); }; @@ -12484,9 +12526,22 @@ LGraphNode.prototype.executeAction = function(action) } LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.graph.beforeChange(node); - node.collapse(); - node.graph.afterChange(node); + node.graph.beforeChange(/*?*/); + + var fApplyMultiNode = function(node){ + node.collapse(); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/*?*/); }; LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { @@ -12504,12 +12559,23 @@ LGraphNode.prototype.executeAction = function(action) return; } var kV = Object.values(LiteGraph.NODE_MODES).indexOf(v); - if (kV>=0 && LiteGraph.NODE_MODES[kV]) - node.changeMode(kV); - else{ - console.warn("unexpected mode: "+v); - node.changeMode(LiteGraph.ALWAYS); - } + var fApplyMultiNode = function(node){ + if (kV>=0 && LiteGraph.NODE_MODES[kV]) + node.changeMode(kV); + else{ + console.warn("unexpected mode: "+v); + node.changeMode(LiteGraph.ALWAYS); + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } } return false; @@ -12555,17 +12621,29 @@ LGraphNode.prototype.executeAction = function(action) } var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } + + var fApplyColor = function(node){ + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyColor(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyColor(graphcanvas.selected_nodes[i]); + } + } node.setDirtyCanvas(true, true); } @@ -12588,9 +12666,22 @@ LGraphNode.prototype.executeAction = function(action) if (!node) { return; } - node.graph.beforeChange(node); - node.shape = v; - node.graph.afterChange(node); + node.graph.beforeChange(/*?*/); //node + + var fApplyMultiNode = function(node){ + node.shape = v; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/*?*/); //node node.setDirtyCanvas(true); } @@ -12602,13 +12693,26 @@ LGraphNode.prototype.executeAction = function(action) throw "no node passed"; } - if (node.removable === false) { - return; - } - var graph = node.graph; graph.beforeChange(); - graph.remove(node); + + + var fApplyMultiNode = function(node){ + if (node.removable === false) { + return; + } + graph.remove(node); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + graph.afterChange(); node.setDirtyCanvas(true, true); }; @@ -12634,17 +12738,37 @@ LGraphNode.prototype.executeAction = function(action) }; LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - if (node.clonable == false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - + node.graph.beforeChange(); - node.graph.add(newnode); + + var newSelected = {}; + + var fApplyMultiNode = function(node){ + if (node.clonable == false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + newSelected[newnode.id] = newnode; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + if(Object.keys(newSelected).length){ + graphcanvas.selectNodes(newSelected); + } + node.graph.afterChange(); node.setDirtyCanvas(true, true); @@ -12668,6 +12792,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.getCanvasMenuOptions = function() { var options = null; + var that = this; if (this.getMenuOptions) { options = this.getMenuOptions(); } else { @@ -12677,12 +12802,13 @@ LGraphNode.prototype.executeAction = function(action) has_submenu: true, callback: LGraphCanvas.onMenuAdd }, - { content: "Add Group", callback: LGraphCanvas.onGroupAdd } + { content: "Add Group", callback: LGraphCanvas.onGroupAdd }, + //{ content: "Arrange", callback: that.graph.arrange }, //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } ]; - if (LiteGraph.showCanvasOptions){ - options.push({ content: "Options", callback: thisGCanvas.showShowGraphOptionsPanel }); - } + /*if (LiteGraph.showCanvasOptions){ + options.push({ content: "Options", callback: that.showShowGraphOptionsPanel }); + }*/ if (this._graph_stack && this._graph_stack.length > 0) { options.push(null, { @@ -12740,7 +12866,7 @@ LGraphNode.prototype.executeAction = function(action) }]; if(node.resizable !== false){ options.push({ - content: "Resize", callback: LGraphCanvas.onResizeNode + content: "Resize", callback: LGraphCanvas.onMenuResizeNode }); } options.push(