[groups] Add undo support for group actions

This commit is contained in:
Nick O'Leary 2020-03-13 23:01:01 +00:00
parent 0ef3471f8f
commit 7886e5d57c
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
4 changed files with 174 additions and 36 deletions

View File

@ -278,6 +278,13 @@ RED.history = (function() {
RED.nodes.addLink(ev.removedLinks[i]); RED.nodes.addLink(ev.removedLinks[i]);
} }
} }
if (ev.addToGroup) {
RED.group.removeFromGroup(ev.addToGroup,ev.nodes.map(function(n) { return n.n }));
inverseEv.removeFromGroup = ev.addToGroup;
} else if (ev.removeFromGroup) {
RED.group.addToGroup(ev.removeFromGroup,ev.nodes.map(function(n) { return n.n }));
inverseEv.addToGroup = ev.removeFromGroup;
}
} else if (ev.t == "edit") { } else if (ev.t == "edit") {
inverseEv = { inverseEv = {
t: "edit", t: "edit",
@ -469,7 +476,52 @@ RED.history = (function() {
RED.workspaces.order(ev.order); RED.workspaces.order(ev.order);
} }
} else if (ev.t == "createGroup") { } else if (ev.t == "createGroup") {
inverseEv = {
t: "ungroup",
dirty: RED.nodes.dirty(),
groups: []
}
if (ev.groups) {
for (i=0;i<ev.groups.length;i++) {
inverseEv.groups.push(ev.groups[i]);
RED.group.ungroup(ev.groups[i]);
}
}
} else if (ev.t == "ungroup") {
inverseEv = {
t: "createGroup",
dirty: RED.nodes.dirty(),
groups: []
}
if (ev.groups) {
for (i=0;i<ev.groups.length;i++) {
inverseEv.groups.push(ev.groups[i]);
var nodes = ev.groups[i].nodes.slice();
ev.groups[i].nodes = [];
RED.nodes.addGroup(ev.groups[i]);
RED.group.addToGroup(ev.groups[i],nodes);
}
}
} else if (ev.t == "addToGroup") {
inverse = {
t: "removeFromGroup",
dirty: RED.nodes.dirty(),
group: ev.group,
nodes: ev.nodes
}
if (ev.nodes) {
RED.group.removeFromGroup(ev.group,ev.nodes);
}
} else if (ev.t == "removeFromGroup") {
inverse = {
t: "addToGroup",
dirty: RED.nodes.dirty(),
group: ev.group,
nodes: ev.nodes
}
if (ev.nodes) {
RED.group.addToGroup(ev.group,ev.nodes);
}
} }
Object.keys(modifiedTabs).forEach(function(id) { Object.keys(modifiedTabs).forEach(function(id) {

View File

@ -343,6 +343,7 @@ RED.nodes = (function() {
var removedNodes = []; var removedNodes = [];
var removedLinks = []; var removedLinks = [];
var removedGroups = [];
var n; var n;
var node; var node;
for (n=0;n<nodes.length;n++) { for (n=0;n<nodes.length;n++) {
@ -359,11 +360,14 @@ RED.nodes = (function() {
} }
} }
} }
removedGroups = groupsByZ[id] || [];
delete groupsByZ[id];
for (n=0;n<removedNodes.length;n++) { for (n=0;n<removedNodes.length;n++) {
var result = removeNode(removedNodes[n].id); var result = removeNode(removedNodes[n].id);
removedLinks = removedLinks.concat(result.links); removedLinks = removedLinks.concat(result.links);
} }
return {nodes:removedNodes,links:removedLinks}; return {nodes:removedNodes,links:removedLinks, groups: removedGroups};
} }
function addSubflow(sf, createNewIds) { function addSubflow(sf, createNewIds) {

View File

@ -74,7 +74,14 @@ RED.group = (function() {
if (selection.nodes) { if (selection.nodes) {
var group = createGroup(selection.nodes); var group = createGroup(selection.nodes);
if (group) { if (group) {
RED.view.select({nodes:[group]}) var historyEvent = {
t:"createGroup",
groups: [ group ],
dirty: RED.nodes.dirty()
}
RED.history.push(historyEvent);
RED.view.select({nodes:[group]});
RED.nodes.dirty(true);
} }
} }
} }
@ -83,10 +90,22 @@ RED.group = (function() {
if (selection.nodes) { if (selection.nodes) {
var newSelection = []; var newSelection = [];
groups = selection.nodes.filter(function(n) { return n.type === "group" }); groups = selection.nodes.filter(function(n) { return n.type === "group" });
var historyEvent = {
t:"ungroup",
groups: [ ],
dirty: RED.nodes.dirty()
}
RED.history.push(historyEvent);
groups.forEach(function(g) { groups.forEach(function(g) {
newSelection = newSelection.concat(ungroup(g)) newSelection = newSelection.concat(ungroup(g))
historyEvent.groups.push(g);
}) })
RED.history.push(historyEvent);
RED.view.select({nodes:newSelection}) RED.view.select({nodes:newSelection})
RED.nodes.dirty(true);
} }
} }
@ -115,6 +134,17 @@ RED.group = (function() {
var selection = RED.view.selection(); var selection = RED.view.selection();
if (selection.nodes) { if (selection.nodes) {
var nodes = []; var nodes = [];
var historyEvent = {
t: "multi",
events: []
}
var ungroupHistoryEvent = {
t: "ungroup",
groups: []
}
var n; var n;
var parentGroup; var parentGroup;
// First pass, check they are all in the same parent // First pass, check they are all in the same parent
@ -133,18 +163,28 @@ RED.group = (function() {
for (var i=0; i<selection.nodes.length; i++) { for (var i=0; i<selection.nodes.length; i++) {
n = selection.nodes[i]; n = selection.nodes[i];
if (n.type === "group") { if (n.type === "group") {
ungroupHistoryEvent.groups.push(n);
nodes = nodes.concat(ungroup(n)); nodes = nodes.concat(ungroup(n));
} else { } else {
nodes.push(n); nodes.push(n);
} }
n.dirty = true; n.dirty = true;
} }
if (ungroupHistoryEvent.groups.length > 0) {
historyEvent.events.push(ungroupHistoryEvent);
}
// Finally, create the new group // Finally, create the new group
var group = createGroup(nodes); var group = createGroup(nodes);
if (group) { if (group) {
RED.view.select({nodes:[group]}) RED.view.select({nodes:[group]})
} }
historyEvent.events.push({
t:"createGroup",
groups: [ group ],
dirty: RED.nodes.dirty()
});
RED.history.push(historyEvent);
RED.nodes.dirty(true);
} }
} }
@ -153,37 +193,24 @@ RED.group = (function() {
if (selection.nodes) { if (selection.nodes) {
var nodes = []; var nodes = [];
var n; var n;
var parentGroup; var parentGroup = RED.nodes.group(selection.nodes[0].g);
// First pass, check they are all in the same parent if (parentGroup) {
// TODO: DRY mergeSelection,removeSelection,... try {
for (var i=0; i<selection.nodes.length; i++) { removeFromGroup(parentGroup,selection.nodes);
n = selection.nodes[i]; var historyEvent = {
if (i === 0) { t: "removeFromGroup",
parentGroup = n.g; dirty: RED.nodes.dirty(),
} else if (n.g !== parentGroup) { group: parentGroup,
RED.notify("Cannot merge nodes from different groups","error"); nodes: selection.nodes
}
RED.history.push(historyEvent);
RED.nodes.dirty(true);
} catch(err) {
RED.notify(err,"error");
return; return;
} }
} }
if (parentGroup) { RED.view.select({nodes:selection.nodes})
parentGroup = RED.nodes.group(parentGroup);
var grandparentGroup = RED.nodes.group(parentGroup.g);
for (var i=0; i<selection.nodes.length; i++) {
n = selection.nodes[i];
n.dirty = true;
nodes.push(n);
var index = parentGroup.nodes.indexOf(n);
parentGroup.nodes.splice(index,1);
if (parentGroup.g) {
n.g = parentGroup.g
grandparentGroup.nodes.push(n);
grandparentGroup.dirty = true;
} else {
delete n.g;
}
}
RED.view.select({nodes:nodes})
}
} }
} }
function createGroup(nodes) { function createGroup(nodes) {
@ -217,7 +244,6 @@ RED.group = (function() {
RED.nodes.addGroup(group); RED.nodes.addGroup(group);
return group; return group;
} }
function addToGroup(group,nodes) { function addToGroup(group,nodes) {
if (!Array.isArray(nodes)) { if (!Array.isArray(nodes)) {
nodes = [nodes]; nodes = [nodes];
@ -274,6 +300,37 @@ RED.group = (function() {
group.h = Math.max(group.h,n.y+n.h/2+25-group.y); group.h = Math.max(group.h,n.y+n.h/2+25-group.y);
} }
} }
function removeFromGroup(group, nodes) {
if (!Array.isArray(nodes)) {
nodes = [nodes];
}
var n;
// First pass, check they are all in the same parent
// TODO: DRY mergeSelection,removeSelection,...
for (var i=0; i<nodes.length; i++) {
if (nodes[i].g !== group.id) {
RED.notify("Cannot remove node "+nodes[i].id+" from group it isn't in");
return;
}
}
var parentGroup = RED.nodes.group(group.g);
for (var i=0; i<nodes.length; i++) {
n = nodes[i];
n.dirty = true;
var index = group.nodes.indexOf(n);
group.nodes.splice(index,1);
group.dirty = true;
if (group.g) {
n.g = group.g
parentGroup.nodes.push(n);
parentGroup.dirty = true;
} else {
delete n.g;
}
}
}
function getNodes(group,recursive) { function getNodes(group,recursive) {
var nodes = []; var nodes = [];
@ -312,7 +369,9 @@ RED.group = (function() {
def: groupDef, def: groupDef,
init: init, init: init,
createGroup: createGroup, createGroup: createGroup,
ungroup: ungroup,
addToGroup: addToGroup, addToGroup: addToGroup,
removeFromGroup: removeFromGroup,
getNodes: getNodes, getNodes: getNodes,
contains: groupContains contains: groupContains
} }

View File

@ -1008,6 +1008,17 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseDown", mouse_mode); }
} }
if (targetGroup) { if (targetGroup) {
RED.group.addToGroup(targetGroup, nn); RED.group.addToGroup(targetGroup, nn);
if (historyEvent.t !== "multi") {
historyEvent = {
t:'multi',
events: [historyEvent]
}
}
historyEvent.events.push({
t: "addToGroup",
group: targetGroup,
nodes: nn
})
} }
@ -1482,11 +1493,14 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); }
} }
if (mouse_mode == RED.state.MOVING_ACTIVE) { if (mouse_mode == RED.state.MOVING_ACTIVE) {
if (moving_set.length > 0) { if (moving_set.length > 0) {
var addedToGroup = null;
if (activeHoverGroup) { if (activeHoverGroup) {
for (var j=0;j<moving_set.length;j++) { for (var j=0;j<moving_set.length;j++) {
var n = moving_set[j]; var n = moving_set[j];
RED.group.addToGroup(activeHoverGroup,n.n); RED.group.addToGroup(activeHoverGroup,n.n);
} }
addedToGroup = activeHoverGroup;
activeHoverGroup.hovered = false; activeHoverGroup.hovered = false;
activeHoverGroup.dirty = true; activeHoverGroup.dirty = true;
activeHoverGroup.active = true; activeHoverGroup.active = true;
@ -1527,6 +1541,9 @@ if (DEBUG_EVENTS) { console.warn("canvasMouseUp", mouse_mode); }
historyEvent.removedLinks = [spliceLink]; historyEvent.removedLinks = [spliceLink];
updateActiveNodes(); updateActiveNodes();
} }
if (addedToGroup) {
historyEvent.addToGroup = addedToGroup;
}
RED.nodes.dirty(true); RED.nodes.dirty(true);
RED.history.push(historyEvent); RED.history.push(historyEvent);
} }
@ -1813,6 +1830,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
dirty: RED.nodes.dirty(), dirty: RED.nodes.dirty(),
nodes: [], nodes: [],
links: [], links: [],
groups: [],
workspaces: [], workspaces: [],
subflows: [] subflows: []
} }
@ -1832,6 +1850,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
} }
historyEvent.nodes = historyEvent.nodes.concat(subEvent.nodes); historyEvent.nodes = historyEvent.nodes.concat(subEvent.nodes);
historyEvent.links = historyEvent.links.concat(subEvent.links); historyEvent.links = historyEvent.links.concat(subEvent.links);
historyEvent.groups = historyEvent.groups.concat(subEvent.groups);
} }
RED.history.push(historyEvent); RED.history.push(historyEvent);
RED.nodes.dirty(true); RED.nodes.dirty(true);
@ -1842,9 +1861,9 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
var result; var result;
var removedNodes = []; var removedNodes = [];
var removedLinks = []; var removedLinks = [];
var removedGroups = [];
var removedSubflowOutputs = []; var removedSubflowOutputs = [];
var removedSubflowInputs = []; var removedSubflowInputs = [];
var removedGroups = [];
var removedSubflowStatus; var removedSubflowStatus;
var subflowInstances = []; var subflowInstances = [];
@ -1911,7 +1930,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
subflowInstances = instances.instances; subflowInstances = instances.instances;
} }
moving_set = []; moving_set = [];
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus) { if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus || removedGroups.length > 0) {
RED.nodes.dirty(true); RED.nodes.dirty(true);
} }
} }
@ -1964,6 +1983,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
t:"delete", t:"delete",
nodes:removedNodes, nodes:removedNodes,
links:removedLinks, links:removedLinks,
groups: removedGroups,
subflowOutputs:removedSubflowOutputs, subflowOutputs:removedSubflowOutputs,
subflowInputs:removedSubflowInputs, subflowInputs:removedSubflowInputs,
subflow: { subflow: {
@ -2005,7 +2025,9 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
} }
var groups = activeGroups.filter(function(g) { return g.selected && !g.active }); var groups = activeGroups.filter(function(g) { return g.selected && !g.active });
while (groups.length > 0) { while (groups.length > 0) {
console.log("GROUPS",groups.slice())
var group = groups.shift(); var group = groups.shift();
console.log("GROUP",group);
nodes.push(group); nodes.push(group);
groups = groups.concat(group.nodes.filter(function(n) { return n.type === "group" })) groups = groups.concat(group.nodes.filter(function(n) { return n.type === "group" }))
} }
@ -2033,7 +2055,7 @@ if (DEBUG_EVENTS) { console.warn("clearSelection", mouse_mode); }
} }
} }
clipboard = JSON.stringify(nns); clipboard = JSON.stringify(nns);
console.log(nns); console.warn(nns);
RED.notify(RED._("clipboard.nodeCopied",{count:nns.length}),{id:"clipboard"}); RED.notify(RED._("clipboard.nodeCopied",{count:nns.length}),{id:"clipboard"});
} }
} }
@ -4486,6 +4508,7 @@ if (DEBUG_EVENTS) { console.warn("nodeMouseDown", mouse_mode,d); }
} }
return result; return result;
}, },
getGroupAtPoint: getGroupAt,
reveal: function(id) { reveal: function(id) {
if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) { if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) {
RED.workspaces.show(id); RED.workspaces.show(id);