1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge pull request #3294 from node-red/multi-select-links

Allow multiple links to be selected by ctrl-click
This commit is contained in:
Nick O'Leary 2022-01-12 17:51:15 +00:00 committed by GitHub
commit fad708e8de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -63,7 +63,6 @@ RED.view = (function() {
var activeGroups = []; var activeGroups = [];
var dirtyGroups = {}; var dirtyGroups = {};
var selected_link = null;
var mousedown_link = null; var mousedown_link = null;
var mousedown_node = null; var mousedown_node = null;
var mousedown_group = null; var mousedown_group = null;
@ -159,6 +158,31 @@ RED.view = (function() {
return api; return api;
})(); })();
var selectedLinks = (function() {
var links = new Set();
return {
add: function(link) {
links.add(link);
link.selected = true;
},
remove: function(link) {
links.delete(link);
link.selected = false;
},
clear: function() {
links.forEach(function(link) { link.selected = false })
links.clear();
},
length: function() {
return links.size;
},
forEach: function(func) { links.forEach(func) },
has: function(link) { return links.has(link) },
toArray: function() { return Array.from(links) }
}
})();
function init() { function init() {
chart = $("#red-ui-workspace-chart"); chart = $("#red-ui-workspace-chart");
@ -909,7 +933,7 @@ RED.view = (function() {
return; return;
} }
if (!mousedown_node && !mousedown_link && !mousedown_group) { if (!mousedown_node && !mousedown_link && !mousedown_group) {
selected_link = null; selectedLinks.clear();
updateSelection(); updateSelection();
} }
if (mouse_mode === 0) { if (mouse_mode === 0) {
@ -1348,7 +1372,7 @@ RED.view = (function() {
return; return;
} }
if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && !mousedown_group && selected_link == null) { if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && !mousedown_group && selectedLinks.length() === 0) {
return; return;
} }
@ -1372,16 +1396,18 @@ RED.view = (function() {
// Get all the wires we need to detach. // Get all the wires we need to detach.
var links = []; var links = [];
var existingLinks = []; var existingLinks = [];
if (selected_link && if (selectedLinks.length() > 0) {
((mousedown_port_type === PORT_TYPE_OUTPUT && selectedLinks.forEach(function(link) {
selected_link.source === mousedown_node && if (((mousedown_port_type === PORT_TYPE_OUTPUT &&
selected_link.sourcePort === mousedown_port_index link.source === mousedown_node &&
link.sourcePort === mousedown_port_index
) || ) ||
(mousedown_port_type === PORT_TYPE_INPUT && (mousedown_port_type === PORT_TYPE_INPUT &&
selected_link.target === mousedown_node link.target === mousedown_node
)) ))) {
) { existingLinks.push(link);
existingLinks = [selected_link]; }
})
} else { } else {
var filter; var filter;
if (mousedown_port_type === PORT_TYPE_OUTPUT) { if (mousedown_port_type === PORT_TYPE_OUTPUT) {
@ -1419,7 +1445,7 @@ RED.view = (function() {
} else if (mousedown_node && !quickAddLink) { } else if (mousedown_node && !quickAddLink) {
showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]); showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]);
} }
selected_link = null; selectedLinks.clear();
} }
mousePos = mouse_position; mousePos = mouse_position;
for (i=0;i<drag_lines.length;i++) { for (i=0;i<drag_lines.length;i++) {
@ -1941,7 +1967,7 @@ RED.view = (function() {
} }
} }
} }
selected_link = null; selectedLinks.clear();
if (mouse_mode !== RED.state.SELECTING_NODE) { if (mouse_mode !== RED.state.SELECTING_NODE) {
updateSelection(); updateSelection();
} }
@ -1956,7 +1982,7 @@ RED.view = (function() {
n.n.selected = false; n.n.selected = false;
} }
movingSet.clear(); movingSet.clear();
selected_link = null; selectedLinks.clear();
if (activeGroup) { if (activeGroup) {
activeGroup.active = false activeGroup.active = false
activeGroup.dirty = true; activeGroup.dirty = true;
@ -2050,12 +2076,16 @@ RED.view = (function() {
} }
} }
} }
if (activeFlowLinks.length === 0 && selected_link !== null && selected_link.link) { if (activeFlowLinks.length === 0 && selectedLinks.length() > 0) {
activeLinks.push(selected_link); selectedLinks.forEach(function(link) {
activeLinkNodes[selected_link.source.id] = selected_link.source; if (link.link) {
selected_link.source.dirty = true; activeLinks.push(link);
activeLinkNodes[selected_link.target.id] = selected_link.target; activeLinkNodes[link.source.id] = link.source;
selected_link.target.dirty = true; link.source.dirty = true;
activeLinkNodes[link.target.id] = link.target;
link.target.dirty = true;
}
})
} }
} else { } else {
selection.flows = workspaceSelection; selection.flows = workspaceSelection;
@ -2066,6 +2096,10 @@ RED.view = (function() {
return value.map(function(n) { return n.id }) return value.map(function(n) { return n.id })
} else if (key === 'link') { } else if (key === 'link') {
return value.source.id+":"+value.sourcePort+":"+value.target.id; return value.source.id+":"+value.sourcePort+":"+value.target.id;
} else if (key === 'links') {
return value.map(function(link) {
return link.source.id+":"+link.sourcePort+":"+link.target.id;
});
} }
return value; return value;
}); });
@ -2135,7 +2169,7 @@ RED.view = (function() {
updateActiveNodes(); updateActiveNodes();
updateSelection(); updateSelection();
redraw(); redraw();
} else if (movingSet.length() > 0 || selected_link != null) { } else if (movingSet.length() > 0 || selectedLinks.length() > 0) {
var result; var result;
var node; var node;
var removedNodes = []; var removedNodes = [];
@ -2234,52 +2268,55 @@ RED.view = (function() {
RED.nodes.dirty(true); RED.nodes.dirty(true);
} }
} }
var historyEvent;
if (selected_link && selected_link.link) { var linkRemoveHistoryEvents = [];
var sourceId = selected_link.source.id; if (selectedLinks.length() > 0) {
var targetId = selected_link.target.id; selectedLinks.forEach(function(link) {
var sourceIdIndex = selected_link.target.links.indexOf(sourceId); if (link.link) {
var targetIdIndex = selected_link.source.links.indexOf(targetId); var sourceId = link.source.id;
var targetId = link.target.id;
var sourceIdIndex = link.target.links.indexOf(sourceId);
var targetIdIndex = link.source.links.indexOf(targetId);
historyEvent = { linkRemoveHistoryEvents.push({
t:"multi", t:"multi",
events: [ events: [
{ {
t: "edit", t: "edit",
node: selected_link.source, node: link.source,
changed: selected_link.source.changed, changed: link.source.changed,
changes: { changes: {
links: $.extend(true,{},{v:selected_link.source.links}).v links: $.extend(true,{},{v:link.source.links}).v
} }
}, },
{ {
t: "edit", t: "edit",
node: selected_link.target, node: link.target,
changed: selected_link.target.changed, changed: link.target.changed,
changes: { changes: {
links: $.extend(true,{},{v:selected_link.target.links}).v links: $.extend(true,{},{v:link.target.links}).v
} }
} }
], ],
dirty:RED.nodes.dirty() dirty:startDirty
} })
RED.nodes.dirty(true);
selected_link.source.changed = true; link.source.changed = true;
selected_link.target.changed = true; link.target.changed = true;
selected_link.target.links.splice(sourceIdIndex,1); link.target.links.splice(sourceIdIndex,1);
selected_link.source.links.splice(targetIdIndex,1); link.source.links.splice(targetIdIndex,1);
selected_link.source.dirty = true; link.source.dirty = true;
selected_link.target.dirty = true; link.target.dirty = true;
} else { } else {
if (selected_link) { RED.nodes.removeLink(link);
RED.nodes.removeLink(selected_link); removedLinks.push(link);
removedLinks.push(selected_link); }
})
} }
RED.nodes.dirty(true); RED.nodes.dirty(true);
historyEvent = { var historyEvent = {
t:"delete", t:"delete",
nodes:removedNodes, nodes:removedNodes,
links:removedLinks, links:removedLinks,
@ -2295,10 +2332,17 @@ RED.view = (function() {
if (removedSubflowStatus) { if (removedSubflowStatus) {
historyEvent.subflow.status = removedSubflowStatus; historyEvent.subflow.status = removedSubflowStatus;
} }
} if (linkRemoveHistoryEvents.length > 0) {
linkRemoveHistoryEvents.unshift(historyEvent);
RED.history.push({
t:"multi",
events: linkRemoveHistoryEvents
})
} else {
RED.history.push(historyEvent); RED.history.push(historyEvent);
}
selected_link = null; selectedLinks.clear();
updateActiveNodes(); updateActiveNodes();
updateSelection(); updateSelection();
redraw(); redraw();
@ -2709,10 +2753,13 @@ RED.view = (function() {
} else { } else {
resetMouseVars(); resetMouseVars();
} }
selected_link = select_link;
mousedown_link = select_link; mousedown_link = select_link;
if (select_link) { if (select_link) {
selectedLinks.clear();
selectedLinks.add(select_link);
updateSelection(); updateSelection();
} else {
selectedLinks.clear();
} }
} }
redraw(); redraw();
@ -2721,7 +2768,10 @@ RED.view = (function() {
resetMouseVars(); resetMouseVars();
hideDragLines(); hideDragLines();
selected_link = select_link; if (select_link) {
selectedLinks.clear();
selectedLinks.add(select_link);
}
mousedown_link = select_link; mousedown_link = select_link;
if (select_link) { if (select_link) {
updateSelection(); updateSelection();
@ -3212,7 +3262,7 @@ RED.view = (function() {
mousedown_node.selected = true; mousedown_node.selected = true;
movingSet.add(mousedown_node); movingSet.add(mousedown_node);
} }
selected_link = null; selectedLinks.clear();
if (d3.event.button != 2) { if (d3.event.button != 2) {
mouse_mode = RED.state.MOVING; mouse_mode = RED.state.MOVING;
var mouse = d3.touches(this)[0]||d3.mouse(this); var mouse = d3.touches(this)[0]||d3.mouse(this);
@ -3339,17 +3389,30 @@ RED.view = (function() {
return; return;
} }
mousedown_link = d; mousedown_link = d;
if (selectedLinks.length() === 0 || !(d3.event.metaKey || d3.event.ctrlKey)) {
clearSelection(); clearSelection();
selected_link = mousedown_link; }
if (d3.event.metaKey || d3.event.ctrlKey) {
if (!selectedLinks.has(mousedown_link)) {
selectedLinks.add(mousedown_link);
} else {
if (selectedLinks.length() !== 1) {
selectedLinks.remove(mousedown_link);
}
}
} else {
selectedLinks.add(mousedown_link);
}
updateSelection(); updateSelection();
redraw(); redraw();
focusView(); focusView();
d3.event.stopPropagation(); d3.event.stopPropagation();
if (d3.event.metaKey || d3.event.ctrlKey) { if (!mousedown_link.link && selectedLinks.length() === 1 && selectedLinks.has(mousedown_link) && (d3.event.metaKey || d3.event.ctrlKey)) {
d3.select(this).classed("red-ui-flow-link-splice",true); d3.select(this).classed("red-ui-flow-link-splice",true);
var point = d3.mouse(this); var point = d3.mouse(this);
var clickedGroup = getGroupAt(point[0],point[1]); var clickedGroup = getGroupAt(point[0],point[1]);
showQuickAddDialog({position:point, splice:selected_link, group:clickedGroup}); showQuickAddDialog({position:point, splice:mousedown_link, group:clickedGroup});
} }
} }
function linkTouchStart(d) { function linkTouchStart(d) {
@ -3359,7 +3422,8 @@ RED.view = (function() {
} }
mousedown_link = d; mousedown_link = d;
clearSelection(); clearSelection();
selected_link = mousedown_link; selectedLinks.clear();
selectedLinks.add(mousedown_link);
updateSelection(); updateSelection();
redraw(); redraw();
focusView(); focusView();
@ -3595,7 +3659,7 @@ RED.view = (function() {
function showTouchMenu(obj,pos) { function showTouchMenu(obj,pos) {
var mdn = mousedown_node; var mdn = mousedown_node;
var options = []; var options = [];
options.push({name:"delete",disabled:(movingSet.length()===0 && selected_link === null),onselect:function() {deleteSelection();}}); options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}});
options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}}); options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}});
options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}}); options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}});
options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}}); options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}});
@ -4352,7 +4416,7 @@ RED.view = (function() {
link.exit().remove(); link.exit().remove();
link.each(function(d) { link.each(function(d) {
var link = d3.select(this); var link = d3.select(this);
if (d.added || d===selected_link || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) { if (d.added || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) {
var numOutputs = d.source.outputs || 1; var numOutputs = d.source.outputs || 1;
var sourcePort = d.sourcePort || 0; var sourcePort = d.sourcePort || 0;
var y = -((numOutputs-1)/2)*13 +13*sourcePort; var y = -((numOutputs-1)/2)*13 +13*sourcePort;
@ -4375,7 +4439,8 @@ RED.view = (function() {
this.__pathLine__.classList.toggle("red-ui-flow-node-disabled",!!(d.source.d || d.target.d)); this.__pathLine__.classList.toggle("red-ui-flow-node-disabled",!!(d.source.d || d.target.d));
this.__pathLine__.classList.toggle("red-ui-flow-subflow-link", !d.link && activeSubflow); this.__pathLine__.classList.toggle("red-ui-flow-subflow-link", !d.link && activeSubflow);
} }
this.classList.toggle("red-ui-flow-link-selected", !!(d===selected_link||d.selected));
this.classList.toggle("red-ui-flow-link-selected", !!d.selected);
var connectedToUnknown = !!(d.target.type == "unknown" || d.source.type == "unknown"); var connectedToUnknown = !!(d.target.type == "unknown" || d.source.type == "unknown");
this.classList.toggle("red-ui-flow-link-unknown",!!(d.target.type == "unknown" || d.source.type == "unknown")) this.classList.toggle("red-ui-flow-link-unknown",!!(d.target.type == "unknown" || d.source.type == "unknown"))
@ -5083,8 +5148,9 @@ RED.view = (function() {
if (allNodes.size > 0) { if (allNodes.size > 0) {
selection.nodes = Array.from(allNodes); selection.nodes = Array.from(allNodes);
} }
if (selected_link != null) { if (selectedLinks.length() > 0) {
selection.link = selected_link; selection.links = selectedLinks.toArray();
selection.link = selection.links[0];
} }
return selection; return selection;
} }