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

[groups] Overhaul group drag handling for empty groups

This commit is contained in:
Nick O'Leary 2020-03-26 15:27:34 +00:00
parent ef9db701f8
commit 1bdbd31b96
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
5 changed files with 210 additions and 92 deletions

View File

@ -209,8 +209,8 @@ RED.history = (function() {
for (i=0;i<ev.groups.length;i++) {
RED.nodes.addGroup(ev.groups[i])
modifiedTabs[ev.groups[i].z] = true;
inverseEv.groups.push(ev.groups[i].id);
if (!groupsToAdd.has(ev.groups[i].g)) {
inverseEv.groups.push(ev.groups[i]);
if (ev.groups[i].g && !groupsToAdd.has(ev.groups[i].g)) {
group = RED.nodes.group(ev.groups[i].g);
if (group.nodes.indexOf(ev.groups[i]) === -1) {
group.nodes.push(ev.groups[i]);

View File

@ -566,6 +566,10 @@ RED.nodes = (function() {
}
}
if (n.type === "group") {
node.x = n.x;
node.y = n.y;
node.w = n.w;
node.h = n.h;
node.nodes = node.nodes.map(function(n) { return n.id });
}
if (n._def.category != "config") {

View File

@ -31,6 +31,7 @@ RED.group = (function() {
// '<label style="width: 70px;margin-right: 10px " for="node-input-style-fill" data-i18n="editor:common.label.fill"></label>'+
// '</span>'+
// '</div>'+
'<div class="node-input-group-style-tools"><span class="button-group"><button class="red-ui-button red-ui-button-small">Use default style</button><button class="red-ui-button red-ui-button-small">Set as default style</button></span></div>'+
'<div class="form-row" id="node-input-row-style-stroke">'+
'<label>Style</label>'+
@ -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))
}
})

View File

@ -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);
}

View File

@ -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<moving_set.length; i++) {
@ -1318,11 +1322,23 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); }
// mousedown_group.y = mousePos[1] + mousedown_group.dy;
// mousedown_group.dirty = true;
// }
var gridOffset = [0,0];
if (snapGrid != d3.event.shiftKey && moving_set.length > 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<moving_set.length && node.n.type === "group")
if (node.n.type === "group") {
// TODO: Group snap to grid
gridOffset[0] = node.n.x-(gridSize*Math.floor(node.n.x/gridSize))-gridSize/2;
gridOffset[1] = node.n.y-(gridSize*Math.floor(node.n.y/gridSize))-gridSize/2;
} else {
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));
}
if (gridOffset[0] !== 0 || gridOffset[1] !== 0) {
for (i = 0; i<moving_set.length; i++) {
node = moving_set[i];
@ -1334,7 +1350,9 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); }
}
}
}
if ((mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING) && moving_set.length === 1) {
// Check link splice or group-add
if (moving_set.length === 1 && moving_set[0].n.type !== "group") {
node = moving_set[0];
if (spliceActive) {
if (!spliceTimer) {
@ -1450,6 +1468,19 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); }
if (!d3.event.shiftKey) {
clearSelection();
}
activeGroups.forEach(function(g) {
if (!g.selected) {
if (g.x > 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<moving_set.length;i++) {
var node = moving_set[i].n;
node.selected = false;
if (node.type != "subflow") {
if (node.type === "group") {
selectedGroups.push(node);
} else if (node.type != "subflow") {
if (node.x < 0) {
node.x = 25
}
@ -1912,7 +1950,6 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
node.dirty = true;
}
}
var selectedGroups = activeGroups.filter(function(g) { return !g.active && g.selected });
while (selectedGroups.length > 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<nodes.length;n++) {
var node = nodes[n];
// The only time a node.type == subflow can be selected is the
// input/output "proxy" nodes. They cannot be copied.
if (node.type != "subflow") {
if (node.type === "group") {
groupCount++;
} else {
nodeCount++;
}
for (var d in node._def.defaults) {
if (node._def.defaults.hasOwnProperty(d)) {
if (node._def.defaults[d].type) {
@ -2105,7 +2149,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
textDimensionPlaceholder[className].textContent = (str||"");
var w = textDimensionPlaceholder[className].offsetWidth;
var h = textDimensionPlaceholder[className].offsetHeight;
return [offsetW+w,offsetH+h];
return [(offsetW||0)+w,(offsetH||0)+h];
}
var separateTextByLineBreak = [];
@ -2555,11 +2599,10 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseUp", mouse_mode,d); }
// Moving primed, but not active.
if (!groupNodeSelectPrimed && !d.selected && d.g && RED.nodes.group(d.g).selected) {
clearSelection();
activeGroup = RED.nodes.group(d.g);
activeGroup.active = true;
activeGroup.dirty = true;
selectGroup(RED.nodes.group(d.g), false);
enterActiveGroup(RED.nodes.group(d.g))
mousedown_node.selected = true;
moving_set.push({n:mousedown_node});
var mouse = d3.touches(this)[0]||d3.mouse(this);
@ -2603,6 +2646,7 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseUp", mouse_mode,d); }
moving_set[i].dx = moving_set[i].n.x-mouse[0];
moving_set[i].dy = moving_set[i].n.y-mouse[1];
}
mouse_offset = d3.mouse(document.body);
if (isNaN(mouse_offset[0])) {
mouse_offset = d3.touches(document.body)[0];
@ -2900,6 +2944,7 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
g.selected = true;
g.dirty = true;
}
moving_set.push({n:g});
if (includeNodes) {
var currentSet = new Set(moving_set.map(function(n) { return n.n }));
var allNodes = RED.group.getNodes(g,true);
@ -2912,6 +2957,20 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
})
}
}
function enterActiveGroup(group) {
if (activeGroup) {
exitActiveGroup();
}
group.active = true;
group.dirty = true;
activeGroup = group;
for (var i = moving_set.length-1; i >= 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<new_ms.length;i++) {
node = new_ms[i];
@ -4236,8 +4321,13 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
node.n.y -= dy - mouse_position[1];
node.dx = node.n.x - mouse_position[0];
node.dy = node.n.y - mouse_position[1];
minX = Math.min(node.n.x-node_width/2-5,minX);
minY = Math.min(node.n.y-node_height/2-5,minY);
if (node.n.type === "group") {
minX = Math.min(node.n.x-5,minX);
minY = Math.min(node.n.y-5,minY);
} else {
minX = Math.min(node.n.x-node_width/2-5,minX);
minY = Math.min(node.n.y-node_height/2-5,minY);
}
}
for (i=0;i<new_ms.length;i++) {
node = new_ms[i];
@ -4254,6 +4344,18 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
}
}
// for (i=0;i<new_gs.length;i++) {
// group = new_gs[i];
// group.n.selected = true;
// group.n.x -= dx - mouse_position[0];
// group.n.y -= dy - mouse_position[1];
// group.dx = group.n.x - mouse_position[0];
// group.dy = group.n.y - mouse_position[1];
// group.n.x -= minX;
// group.n.y -= minY;
// group.dx -= minX;
// group.dy -= minY;
// }
if (!touchImport) {
mouse_mode = RED.state.IMPORT_DRAGGING;
spliceActive = false;
@ -4372,22 +4474,24 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
var historyEvents = [];
for (var i=0;i<moving_set.length;i++) {
var node = moving_set[i].n;
if (isDisabled != node.d) {
historyEvents.push({
t: "edit",
node: node,
changed: node.changed,
changes: {
d: node.d
if (node.type !== "group" && node.type !== "subflow") {
if (isDisabled != node.d) {
historyEvents.push({
t: "edit",
node: node,
changed: node.changed,
changes: {
d: node.d
}
});
if (isDisabled) {
node.d = true;
} else {
delete node.d;
}
});
if (isDisabled) {
node.d = true;
} else {
delete node.d;
node.dirty = true;
node.changed = true;
}
node.dirty = true;
node.changed = true;
}
}
if (historyEvents.length > 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