diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 1e5878e1b..7de739491 100755 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -358,7 +358,7 @@ RED.view = (function() { var spliceLink = $(ui.helper).data("splice"); if (spliceLink) { - // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp + // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp/showQuickAddDialog RED.nodes.removeLink(spliceLink); var link1 = { source:spliceLink.source, @@ -695,280 +695,8 @@ RED.view = (function() { } if (mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) { if (d3.event.metaKey || d3.event.ctrlKey) { - point = d3.mouse(this); - var ox = point[0]; - var oy = point[1]; - - if (RED.settings.get("editor").view['view-snap-grid']) { - // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red') - point[0] = Math.round(point[0] / gridSize) * gridSize; - point[1] = Math.round(point[1] / gridSize) * gridSize; - // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','blue') - } - d3.event.stopPropagation(); - var mainPos = $("#red-ui-main-container").position(); - - if (mouse_mode !== RED.state.QUICK_JOINING) { - mouse_mode = RED.state.QUICK_JOINING; - $(window).on('keyup',disableQuickJoinEventHandler); - } - quickAddActive = true; - - if (ghostNode) { - ghostNode.remove(); - } - ghostNode = eventLayer.append("g").attr('transform','translate('+(point[0] - node_width/2)+','+(point[1] - node_height/2)+')'); - ghostNode.append("rect") - .attr("class","red-ui-flow-node-placeholder") - .attr("rx", 5) - .attr("ry", 5) - .attr("width",node_width) - .attr("height",node_height) - .attr("fill","none") - // var ghostLink = ghostNode.append("svg:path") - // .attr("class","red-ui-flow-link-link") - // .attr("d","M 0 "+(node_height/2)+" H "+(gridSize * -2)) - // .attr("opacity",0); - - var filter = undefined; - if (drag_lines.length > 0) { - if (drag_lines[0].virtualLink) { - filter = {type:drag_lines[0].node.type === 'link in'?'link out':'link in'} - } else if (drag_lines[0].portType === PORT_TYPE_OUTPUT) { - filter = {input:true} - } else { - filter = {output:true} - } - - quickAddLink = { - node: drag_lines[0].node, - port: drag_lines[0].port, - portType: drag_lines[0].portType, - } - if (drag_lines[0].virtualLink) { - quickAddLink.virtualLink = true; - } - hideDragLines(); - } - var rebuildQuickAddLink = function() { - if (!quickAddLink) { - return; - } - if (!quickAddLink.el) { - quickAddLink.el = dragGroupLayer.append("svg:path").attr("class", "red-ui-flow-drag-line"); - } - var numOutputs = (quickAddLink.portType === PORT_TYPE_OUTPUT)?(quickAddLink.node.outputs || 1):1; - var sourcePort = quickAddLink.port; - var portY = -((numOutputs-1)/2)*13 +13*sourcePort; - var sc = (quickAddLink.portType === PORT_TYPE_OUTPUT)?1:-1; - quickAddLink.el.attr("d",generateLinkPath(quickAddLink.node.x+sc*quickAddLink.node.w/2,quickAddLink.node.y+portY,point[0]-sc*node_width/2,point[1],sc)); - } - if (quickAddLink) { - rebuildQuickAddLink(); - } - - - var lastAddedX; - var lastAddedWidth; - - RED.typeSearch.show({ - x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]), - y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]), - filter: filter, - move: function(dx,dy) { - if (ghostNode) { - var pos = d3.transform(ghostNode.attr("transform")).translate; - ghostNode.attr("transform","translate("+(pos[0]+dx)+","+(pos[1]+dy)+")") - point[0] += dx; - point[1] += dy; - rebuildQuickAddLink(); - } - }, - cancel: function() { - if (quickAddLink) { - if (quickAddLink.el) { - quickAddLink.el.remove(); - } - quickAddLink = null; - } - quickAddActive = false; - if (ghostNode) { - ghostNode.remove(); - } - resetMouseVars(); - updateSelection(); - hideDragLines(); - redraw(); - }, - add: function(type,keepAdding) { - var result = addNode(type); - if (!result) { - return; - } - if (keepAdding) { - mouse_mode = RED.state.QUICK_JOINING; - } - - var nn = result.node; - var historyEvent = result.historyEvent; - nn.x = point[0]; - nn.y = point[1]; - var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label"); - if (showLabel !== undefined && !/^link (in|out)$/.test(nn._def.type) && !nn._def.defaults.hasOwnProperty("l")) { - nn.l = showLabel; - } - if (quickAddLink) { - var drag_line = quickAddLink; - var src = null,dst,src_port; - if (drag_line.portType === PORT_TYPE_OUTPUT && (nn.inputs > 0 || drag_line.virtualLink) ) { - src = drag_line.node; - src_port = drag_line.port; - dst = nn; - } else if (drag_line.portType === PORT_TYPE_INPUT && (nn.outputs > 0 || drag_line.virtualLink)) { - src = nn; - dst = drag_line.node; - src_port = 0; - } - - if (src !== null) { - // Joining link nodes via virual wires. Need to update - // the src and dst links property - if (drag_line.virtualLink) { - historyEvent = { - t:'multi', - events: [historyEvent] - } - var oldSrcLinks = $.extend(true,{},{v:src.links}).v - var oldDstLinks = $.extend(true,{},{v:dst.links}).v - src.links.push(dst.id); - dst.links.push(src.id); - src.dirty = true; - dst.dirty = true; - - historyEvent.events.push({ - t:'edit', - node: src, - dirty: RED.nodes.dirty(), - changed: src.changed, - changes: { - links:oldSrcLinks - } - }); - historyEvent.events.push({ - t:'edit', - node: dst, - dirty: RED.nodes.dirty(), - changed: dst.changed, - changes: { - links:oldDstLinks - } - }); - src.changed = true; - dst.changed = true; - } else { - var link = {source: src, sourcePort:src_port, target: dst}; - RED.nodes.addLink(link); - historyEvent.links = [link]; - } - if (!keepAdding) { - quickAddLink.el.remove(); - quickAddLink = null; - if (mouse_mode === RED.state.QUICK_JOINING) { - if (drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]); - } else if (!quickAddLink && drag_line.portType === PORT_TYPE_INPUT && nn.inputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]); - } else { - resetMouseVars(); - } - } - } else { - quickAddLink.node = nn; - quickAddLink.port = 0; - } - } else { - hideDragLines(); - resetMouseVars(); - } - } else { - if (!keepAdding) { - if (mouse_mode === RED.state.QUICK_JOINING) { - if (nn.outputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]); - } else if (nn.inputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]); - } else { - resetMouseVars(); - } - } - } else { - if (nn.outputs > 0) { - quickAddLink = { - node: nn, - port: 0, - portType: PORT_TYPE_OUTPUT - } - } else if (nn.inputs > 0) { - quickAddLink = { - node: nn, - port: 0, - portType: PORT_TYPE_INPUT - } - } else { - resetMouseVars(); - } - } - } - - RED.history.push(historyEvent); - RED.nodes.add(nn); - RED.editor.validateNode(nn); - RED.nodes.dirty(true); - // auto select dropped node - so info shows (if visible) - clearSelection(); - nn.selected = true; - moving_set.push({n:nn}); - updateActiveNodes(); - updateSelection(); - redraw(); - // At this point the newly added node will have a real width, - // so check if the position needs nudging - if (lastAddedX !== undefined) { - var lastNodeRHEdge = lastAddedX + lastAddedWidth/2; - var thisNodeLHEdge = nn.x - nn.w/2; - var gap = thisNodeLHEdge - lastNodeRHEdge; - if (gap != gridSize *2) { - nn.x = nn.x + gridSize * 2 - gap; - nn.dirty = true; - nn.x = Math.ceil(nn.x / gridSize) * gridSize; - redraw(); - } - } - if (keepAdding) { - if (lastAddedX === undefined) { - // ghostLink.attr("opacity",1); - setTimeout(function() { - RED.typeSearch.refresh({filter:{input:true}}); - },100); - } - - lastAddedX = nn.x; - lastAddedWidth = nn.w; - - point[0] = nn.x + nn.w/2 + node_width/2 + gridSize * 2; - ghostNode.attr('transform','translate('+(point[0] - node_width/2)+','+(point[1] - node_height/2)+')'); - rebuildQuickAddLink(); - } else { - quickAddActive = false; - ghostNode.remove(); - } - } - }); - - updateActiveNodes(); - updateSelection(); - redraw(); + showQuickAddDialog(d3.mouse(this)); } } if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) { @@ -989,6 +717,302 @@ RED.view = (function() { } } + function showQuickAddDialog(point,spliceLink) { + var ox = point[0]; + var oy = point[1]; + + if (RED.settings.get("editor").view['view-snap-grid']) { + // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red') + point[0] = Math.round(point[0] / gridSize) * gridSize; + point[1] = Math.round(point[1] / gridSize) * gridSize; + // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','blue') + } + + var mainPos = $("#red-ui-main-container").position(); + + if (mouse_mode !== RED.state.QUICK_JOINING) { + mouse_mode = RED.state.QUICK_JOINING; + $(window).on('keyup',disableQuickJoinEventHandler); + } + quickAddActive = true; + + if (ghostNode) { + ghostNode.remove(); + } + ghostNode = eventLayer.append("g").attr('transform','translate('+(point[0] - node_width/2)+','+(point[1] - node_height/2)+')'); + ghostNode.append("rect") + .attr("class","red-ui-flow-node-placeholder") + .attr("rx", 5) + .attr("ry", 5) + .attr("width",node_width) + .attr("height",node_height) + .attr("fill","none") + // var ghostLink = ghostNode.append("svg:path") + // .attr("class","red-ui-flow-link-link") + // .attr("d","M 0 "+(node_height/2)+" H "+(gridSize * -2)) + // .attr("opacity",0); + + var filter = undefined; + if (drag_lines.length > 0) { + if (drag_lines[0].virtualLink) { + filter = {type:drag_lines[0].node.type === 'link in'?'link out':'link in'} + } else if (drag_lines[0].portType === PORT_TYPE_OUTPUT) { + filter = {input:true} + } else { + filter = {output:true} + } + + quickAddLink = { + node: drag_lines[0].node, + port: drag_lines[0].port, + portType: drag_lines[0].portType, + } + if (drag_lines[0].virtualLink) { + quickAddLink.virtualLink = true; + } + hideDragLines(); + } else if (spliceLink) { + filter = {input:true, output:true} + } + var rebuildQuickAddLink = function() { + if (!quickAddLink) { + return; + } + if (!quickAddLink.el) { + quickAddLink.el = dragGroupLayer.append("svg:path").attr("class", "red-ui-flow-drag-line"); + } + var numOutputs = (quickAddLink.portType === PORT_TYPE_OUTPUT)?(quickAddLink.node.outputs || 1):1; + var sourcePort = quickAddLink.port; + var portY = -((numOutputs-1)/2)*13 +13*sourcePort; + var sc = (quickAddLink.portType === PORT_TYPE_OUTPUT)?1:-1; + quickAddLink.el.attr("d",generateLinkPath(quickAddLink.node.x+sc*quickAddLink.node.w/2,quickAddLink.node.y+portY,point[0]-sc*node_width/2,point[1],sc)); + } + if (quickAddLink) { + rebuildQuickAddLink(); + } + + + var lastAddedX; + var lastAddedWidth; + + RED.typeSearch.show({ + x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]), + y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]), + filter: filter, + move: function(dx,dy) { + if (ghostNode) { + var pos = d3.transform(ghostNode.attr("transform")).translate; + ghostNode.attr("transform","translate("+(pos[0]+dx)+","+(pos[1]+dy)+")") + point[0] += dx; + point[1] += dy; + rebuildQuickAddLink(); + } + }, + cancel: function() { + if (quickAddLink) { + if (quickAddLink.el) { + quickAddLink.el.remove(); + } + quickAddLink = null; + } + quickAddActive = false; + if (ghostNode) { + ghostNode.remove(); + } + resetMouseVars(); + updateSelection(); + hideDragLines(); + redraw(); + }, + add: function(type,keepAdding) { + var result = addNode(type); + if (!result) { + return; + } + if (keepAdding) { + mouse_mode = RED.state.QUICK_JOINING; + } + + var nn = result.node; + var historyEvent = result.historyEvent; + nn.x = point[0]; + nn.y = point[1]; + var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label"); + if (showLabel !== undefined && !/^link (in|out)$/.test(nn._def.type) && !nn._def.defaults.hasOwnProperty("l")) { + nn.l = showLabel; + } + if (!spliceLink) { + if (quickAddLink) { + var drag_line = quickAddLink; + var src = null,dst,src_port; + if (drag_line.portType === PORT_TYPE_OUTPUT && (nn.inputs > 0 || drag_line.virtualLink) ) { + src = drag_line.node; + src_port = drag_line.port; + dst = nn; + } else if (drag_line.portType === PORT_TYPE_INPUT && (nn.outputs > 0 || drag_line.virtualLink)) { + src = nn; + dst = drag_line.node; + src_port = 0; + } + + if (src !== null) { + // Joining link nodes via virual wires. Need to update + // the src and dst links property + if (drag_line.virtualLink) { + historyEvent = { + t:'multi', + events: [historyEvent] + } + var oldSrcLinks = $.extend(true,{},{v:src.links}).v + var oldDstLinks = $.extend(true,{},{v:dst.links}).v + src.links.push(dst.id); + dst.links.push(src.id); + src.dirty = true; + dst.dirty = true; + + historyEvent.events.push({ + t:'edit', + node: src, + dirty: RED.nodes.dirty(), + changed: src.changed, + changes: { + links:oldSrcLinks + } + }); + historyEvent.events.push({ + t:'edit', + node: dst, + dirty: RED.nodes.dirty(), + changed: dst.changed, + changes: { + links:oldDstLinks + } + }); + src.changed = true; + dst.changed = true; + } else { + var link = {source: src, sourcePort:src_port, target: dst}; + RED.nodes.addLink(link); + historyEvent.links = [link]; + } + if (!keepAdding) { + quickAddLink.el.remove(); + quickAddLink = null; + if (mouse_mode === RED.state.QUICK_JOINING) { + if (drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) { + showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]); + } else if (!quickAddLink && drag_line.portType === PORT_TYPE_INPUT && nn.inputs > 0) { + showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]); + } else { + resetMouseVars(); + } + } + } else { + quickAddLink.node = nn; + quickAddLink.port = 0; + } + } else { + hideDragLines(); + resetMouseVars(); + } + } else { + if (!keepAdding) { + if (mouse_mode === RED.state.QUICK_JOINING) { + if (nn.outputs > 0) { + showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]); + } else if (nn.inputs > 0) { + showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]); + } else { + resetMouseVars(); + } + } + } else { + if (nn.outputs > 0) { + quickAddLink = { + node: nn, + port: 0, + portType: PORT_TYPE_OUTPUT + } + } else if (nn.inputs > 0) { + quickAddLink = { + node: nn, + port: 0, + portType: PORT_TYPE_INPUT + } + } else { + resetMouseVars(); + } + } + } + } else { + resetMouseVars(); + // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp/showQuickAddDialog + RED.nodes.removeLink(spliceLink); + var link1 = { + source:spliceLink.source, + sourcePort:spliceLink.sourcePort, + target: nn + }; + var link2 = { + source:nn, + sourcePort:0, + target: spliceLink.target + }; + RED.nodes.addLink(link1); + RED.nodes.addLink(link2); + historyEvent.links = [link1,link2]; + historyEvent.removedLinks = [spliceLink]; + } + RED.history.push(historyEvent); + RED.nodes.add(nn); + RED.editor.validateNode(nn); + RED.nodes.dirty(true); + // auto select dropped node - so info shows (if visible) + clearSelection(); + nn.selected = true; + moving_set.push({n:nn}); + updateActiveNodes(); + updateSelection(); + redraw(); + // At this point the newly added node will have a real width, + // so check if the position needs nudging + if (lastAddedX !== undefined) { + var lastNodeRHEdge = lastAddedX + lastAddedWidth/2; + var thisNodeLHEdge = nn.x - nn.w/2; + var gap = thisNodeLHEdge - lastNodeRHEdge; + if (gap != gridSize *2) { + nn.x = nn.x + gridSize * 2 - gap; + nn.dirty = true; + nn.x = Math.ceil(nn.x / gridSize) * gridSize; + redraw(); + } + } + if (keepAdding) { + if (lastAddedX === undefined) { + // ghostLink.attr("opacity",1); + setTimeout(function() { + RED.typeSearch.refresh({filter:{input:true}}); + },100); + } + + lastAddedX = nn.x; + lastAddedWidth = nn.w; + + point[0] = nn.x + nn.w/2 + node_width/2 + gridSize * 2; + ghostNode.attr('transform','translate('+(point[0] - node_width/2)+','+(point[1] - node_height/2)+')'); + rebuildQuickAddLink(); + } else { + quickAddActive = false; + ghostNode.remove(); + } + } + }); + + updateActiveNodes(); + updateSelection(); + redraw(); + } + function canvasMouseMove() { var i; var node; @@ -3205,6 +3229,10 @@ RED.view = (function() { redraw(); focusView(); d3.event.stopPropagation(); + if (d3.event.metaKey || d3.event.ctrlKey) { + l.classed("red-ui-flow-link-splice",true); + showQuickAddDialog(d3.mouse(this), selected_link); + } }) .on("touchstart",function(d) { if (mouse_mode === RED.state.SELECTING_NODE) {