From 1bdbd31b96cdb7b417187c292f6fb87c57f88290 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 26 Mar 2020 15:27:34 +0000 Subject: [PATCH] [groups] Overhaul group drag handling for empty groups --- .../@node-red/editor-client/src/js/history.js | 4 +- .../@node-red/editor-client/src/js/nodes.js | 4 + .../editor-client/src/js/ui/group.js | 6 +- .../editor-client/src/js/ui/view-tools.js | 12 +- .../@node-red/editor-client/src/js/ui/view.js | 276 ++++++++++++------ 5 files changed, 210 insertions(+), 92 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 1a673491e..73796e51c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -209,8 +209,8 @@ RED.history = (function() { for (i=0;i'+ // ''+ // ''+ + '
'+ '
'+ ''+ @@ -535,9 +536,8 @@ RED.group = (function() { function getNodes(group,recursive) { var nodes = []; group.nodes.forEach(function(n) { - if (!recursive || n.type !== 'group') { - nodes.push(n); - } else { + nodes.push(n); + if (recursive && n.type === 'group') { nodes = nodes.concat(getNodes(n,recursive)) } }) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 539fb763d..61ab71c72 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -67,9 +67,16 @@ RED.view.tools = (function() { function moveSelection(dx,dy) { if (moving_set === null) { + moving_set = []; var selection = RED.view.selection(); if (selection.nodes) { - moving_set = selection.nodes.map(function(n) { return {n:n}}); + while (selection.nodes.length > 0) { + var n = selection.nodes.shift(); + moving_set.push({n:n}); + if (n.type === "group") { + selection.nodes = selection.nodes.concat(n.nodes); + } + } } } if (moving_set && moving_set.length > 0) { @@ -93,6 +100,9 @@ RED.view.tools = (function() { node.n.x += dx; node.n.y += dy; node.n.dirty = true; + if (node.n.type === "group") { + RED.group.markDirty(node.n); + } minX = Math.min(node.n.x-node.n.w/2-5,minX); minY = Math.min(node.n.y-node.n.h/2-5,minY); } 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 22db1a99b..094e2e0db 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 @@ -415,7 +415,7 @@ RED.view = (function() { moving_set.push({n:nn}); if (group) { selectGroup(group,false); - group.active = true; + enterActiveGroup(group); activeGroup = group; } updateActiveNodes(); @@ -790,10 +790,8 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); } function showQuickAddDialog(point, spliceLink, targetGroup) { if (targetGroup && !targetGroup.active) { - targetGroup.active = true; - targetGroup.dirty = true; selectGroup(targetGroup,false); - activeGroup = targetGroup; + enterActiveGroup(targetGroup); RED.view.redraw(); } @@ -1068,9 +1066,7 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); } nn.selected = true; if (targetGroup) { selectGroup(targetGroup,false); - targetGroup.active = true - targetGroup.dirty = true; - activeGroup = targetGroup; + enterActiveGroup(targetGroup); } moving_set.push({n:nn}); updateActiveNodes(); @@ -1294,10 +1290,18 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); } node.n.x = mousePos[0]+node.dx; node.n.y = mousePos[1]+node.dy; node.n.dirty = true; - minX = Math.min(node.n.x-node.n.w/2-5,minX); - minY = Math.min(node.n.y-node.n.h/2-5,minY); - maxX = Math.max(node.n.x+node.n.w/2+5,maxX); - maxY = Math.max(node.n.y+node.n.h/2+5,maxY); + if (node.n.type === "group") { + RED.group.markDirty(node.n); + minX = Math.min(node.n.x-5,minX); + minY = Math.min(node.n.y-5,minY); + maxX = Math.max(node.n.x+node.n.w+5,maxX); + maxY = Math.max(node.n.y+node.n.h+5,maxY); + } else { + minX = Math.min(node.n.x-node.n.w/2-5,minX); + minY = Math.min(node.n.y-node.n.h/2-5,minY); + maxX = Math.max(node.n.x+node.n.w/2+5,maxX); + maxY = Math.max(node.n.y+node.n.h/2+5,maxY); + } } if (minX !== 0 || minY !== 0) { for (i = 0; i 0) { - var gridOffset = [0,0]; - node = moving_set[0]; - gridOffset[0] = node.n.x-(gridSize*Math.floor((node.n.x-node.n.w/2)/gridSize)+node.n.w/2); - gridOffset[1] = node.n.y-(gridSize*Math.floor(node.n.y/gridSize)); + var i = 0; + + // Prefer to snap nodes to the grid if there is one in the selection + do { + node = moving_set[i++]; + } while(i x && g.x+g.w < x2 && g.y > y && g.y+g.h < y2) { + while (g.g) { + g = RED.nodes.group(g.g); + } + if (!g.selected) { + selectGroup(g,true); + } + } + } + }) + activeNodes.forEach(function(n) { if (!n.selected) { if (n.x > x && n.x < x2 && n.y > y && n.y < y2) { @@ -1469,6 +1500,9 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); } } } }); + + + // var selectionChanged = false; // do { // selectionChanged = false; @@ -1521,11 +1555,9 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); } addedToGroup = activeHoverGroup; activeHoverGroup.hovered = false; - activeHoverGroup.dirty = true; - activeHoverGroup.active = true; - activeGroup = activeHoverGroup; + enterActiveGroup(activeHoverGroup) + // TODO: check back whether this should add to moving_set activeGroup.selected = true; - activeHoverGroup = null; } @@ -1538,6 +1570,7 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); } n.n.moved = true; } } + if (ns.length > 0) { historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()}; if (activeSpliceLink) { @@ -1817,6 +1850,8 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); } var node = moving_set[0].n; if (node.type === "subflow") { RED.editor.editSubflow(activeSubflow); + } else if (node.type === "group") { + RED.editor.editGroup(node); } else { RED.editor.edit(node); } @@ -1882,11 +1917,14 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); } var startDirty = RED.nodes.dirty(); var startChanged = false; + var selectedGroups = []; if (moving_set.length > 0) { for (var i=0;i 0) { var g = selectedGroups.shift(); if (removedGroups.indexOf(g) === -1) { @@ -2040,24 +2077,31 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); } } }); } else { - if (moving_set.length > 0) { - nodes = moving_set.map(function(n) { return n.n }); - } - var groups = activeGroups.filter(function(g) { return g.selected && !g.active }); - while (groups.length > 0) { - var group = groups.shift(); - nodes.push(group); - groups = groups.concat(group.nodes.filter(function(n) { return n.type === "group" })) + selection = RED.view.selection(); + if (selection.nodes) { + selection.nodes.forEach(function(n) { + nodes.push(n); + if (n.type === 'group') { + nodes = nodes.concat(RED.group.getNodes(n,true)); + } + }) } } if (nodes.length > 0) { var nns = []; + var nodeCount = 0; + var groupCount = 0; for (var n=0;n= 0; i -= 1) { + if (moving_set[i].n === group) { + moving_set.splice(i,1); + break; + } + } + } function exitActiveGroup() { if (activeGroup) { activeGroup.active = false; @@ -2927,8 +2986,9 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } g.dirty = true; } var nodeSet = new Set(g.nodes); + nodeSet.add(g); for (var i = moving_set.length-1; i >= 0; i -= 1) { - if (nodeSet.has(moving_set[i].n)) { + if (nodeSet.has(moving_set[i].n) || moving_set[i].n === g) { moving_set[i].n.selected = false; moving_set[i].n.dirty = true; moving_set.splice(i,1); @@ -4072,30 +4132,48 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } group[0].reverse(); group.each(function(d,i) { + if (d.resize) { + d.minWidth = 0; + delete d.resize; + } if (d.dirty || dirtyGroups[d.id]) { var g = d3.select(this); - var minX = Number.POSITIVE_INFINITY; - var minY = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - d.nodes.forEach(function(n) { - if (n.type !== "group") { - minX = Math.min(minX,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0)); - minY = Math.min(minY,n.y-n.h/2-25); - maxX = Math.max(maxX,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0)); - maxY = Math.max(maxY,n.y+n.h/2+25); - } else { - minX = Math.min(minX,n.x-25) - minY = Math.min(minY,n.y-25) - maxX = Math.max(maxX,n.x+n.w+25) - maxY = Math.max(maxY,n.y+n.h+25) - } - }); + if (d.nodes.length > 0) { + var minX = Number.POSITIVE_INFINITY; + var minY = Number.POSITIVE_INFINITY; + var maxX = 0; + var maxY = 0; + d.nodes.forEach(function(n) { + if (n.type !== "group") { + minX = Math.min(minX,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0)); + minY = Math.min(minY,n.y-n.h/2-25); + maxX = Math.max(maxX,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0)); + maxY = Math.max(maxY,n.y+n.h/2+25); + } else { + minX = Math.min(minX,n.x-25) + minY = Math.min(minY,n.y-25) + maxX = Math.max(maxX,n.x+n.w+25) + maxY = Math.max(maxY,n.y+n.h+25) + } + }); + + d.x = minX; + d.y = minY; + d.w = maxX - minX; + d.h = maxY - minY; + } else { + d.w = 40; + d.h = 40; + } + if (!d.minWidth) { + if (d.style.label && d.name) { + d.minWidth = calculateTextWidth(d.name||"","red-ui-flow-group-label",8); + } else { + d.minWidth = 40; + } + } + d.w = Math.max(d.minWidth,d.w); - d.x = minX; - d.y = minY; - d.w = maxX - minX; - d.h = maxY - minY; g.attr("transform","translate("+d.x+","+d.y+")") .classed("red-ui-flow-group-hovered",d.hovered); @@ -4113,10 +4191,10 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } g.selectAll(".red-ui-flow-group-body") .attr("width",d.w) .attr("height",d.h) - .style("stroke", d.style.stroke || "none") - .style("stroke-opacity", d.style.hasOwnProperty('stroke-opacity') ? d.style['stroke-opacity'] : 1) - .style("fill", d.style.fill || "none") - .style("fill-opacity", d.style.hasOwnProperty('fill-opacity') ? d.style['fill-opacity'] : 1) + .style("stroke", d.style.stroke || "") + .style("stroke-opacity", d.style.hasOwnProperty('stroke-opacity') ? d.style['stroke-opacity'] : "") + .style("fill", d.style.fill || "") + .style("fill-opacity", d.style.hasOwnProperty('fill-opacity') ? d.style['fill-opacity'] : "") var label = g.selectAll(".red-ui-flow-group-label"); label.classed("hide",!!!d.style.label) @@ -4210,22 +4288,29 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } RED.workspaces.show(new_default_workspace.id); } var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() }).map(function(n) { return {n:n};}); + new_ms = new_ms.concat(new_groups.map(function(g) { return {n:g}})) var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; }); - + // var new_gs = new_groups.filter(function(g) { console.log(g.id,g.x,g.y); return g.nodes.length === 0}).map(function(g) { return {n:g}}) // TODO: pick a more sensible root node - if (new_ms.length > 0) { - var root_node = new_ms[0].n; - var dx = root_node.x; - var dy = root_node.y; + if (new_ms.length > 0 /* || new_gs.length > 0*/) { + if (mouse_position == null) { mouse_position = [0,0]; } + var dx = mouse_position[0]; + var dy = mouse_position[1]; + if (new_ms.length > 0) { + var root_node = new_ms[0].n; + dx = root_node.x; + dy = root_node.y; + } + var minX = 0; var minY = 0; var i; - var node; + var node,group; for (i=0;i 0) { @@ -4402,8 +4506,6 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } RED.view.redraw(); } - - function getSelection() { var selection = {}; @@ -4411,10 +4513,12 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); } if (moving_set.length > 0) { moving_set.forEach(function(n) { - allNodes.add(n.n); + if (n.n.type !== 'group') { + allNodes.add(n.n); + } }); } - var selectedGroups = activeGroups.filter(function(g) { return g.selected && !g.active }); + var selectedGroups = activeGroups.filter(function(g) { return g.selected && !g.active }); if (selectedGroups.length > 0) { if (selectedGroups.length === 1 && selectedGroups[0].active) { // Let nodes be nodes