[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]);
}
}
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") {
inverseEv = {
t: "edit",
@ -469,7 +476,52 @@ RED.history = (function() {
RED.workspaces.order(ev.order);
}
} 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) {

View File

@ -343,6 +343,7 @@ RED.nodes = (function() {
var removedNodes = [];
var removedLinks = [];
var removedGroups = [];
var n;
var node;
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++) {
var result = removeNode(removedNodes[n].id);
removedLinks = removedLinks.concat(result.links);
}
return {nodes:removedNodes,links:removedLinks};
return {nodes:removedNodes,links:removedLinks, groups: removedGroups};
}
function addSubflow(sf, createNewIds) {

View File

@ -74,7 +74,14 @@ RED.group = (function() {
if (selection.nodes) {
var group = createGroup(selection.nodes);
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) {
var newSelection = [];
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) {
newSelection = newSelection.concat(ungroup(g))
historyEvent.groups.push(g);
})
RED.history.push(historyEvent);
RED.view.select({nodes:newSelection})
RED.nodes.dirty(true);
}
}
@ -115,6 +134,17 @@ RED.group = (function() {
var selection = RED.view.selection();
if (selection.nodes) {
var nodes = [];
var historyEvent = {
t: "multi",
events: []
}
var ungroupHistoryEvent = {
t: "ungroup",
groups: []
}
var n;
var parentGroup;
// 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++) {
n = selection.nodes[i];
if (n.type === "group") {
ungroupHistoryEvent.groups.push(n);
nodes = nodes.concat(ungroup(n));
} else {
nodes.push(n);
}
n.dirty = true;
}
if (ungroupHistoryEvent.groups.length > 0) {
historyEvent.events.push(ungroupHistoryEvent);
}
// Finally, create the new group
var group = createGroup(nodes);
if (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) {
var nodes = [];
var n;
var parentGroup;
// First pass, check they are all in the same parent
// TODO: DRY mergeSelection,removeSelection,...
for (var i=0; i<selection.nodes.length; i++) {
n = selection.nodes[i];
if (i === 0) {
parentGroup = n.g;
} else if (n.g !== parentGroup) {
RED.notify("Cannot merge nodes from different groups","error");
var parentGroup = RED.nodes.group(selection.nodes[0].g);
if (parentGroup) {
try {
removeFromGroup(parentGroup,selection.nodes);
var historyEvent = {
t: "removeFromGroup",
dirty: RED.nodes.dirty(),
group: parentGroup,
nodes: selection.nodes
}
RED.history.push(historyEvent);
RED.nodes.dirty(true);
} catch(err) {
RED.notify(err,"error");
return;
}
}
if (parentGroup) {
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})
}
RED.view.select({nodes:selection.nodes})
}
}
function createGroup(nodes) {
@ -217,7 +244,6 @@ RED.group = (function() {
RED.nodes.addGroup(group);
return group;
}
function addToGroup(group,nodes) {
if (!Array.isArray(nodes)) {
nodes = [nodes];
@ -274,6 +300,37 @@ RED.group = (function() {
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) {
var nodes = [];
@ -312,7 +369,9 @@ RED.group = (function() {
def: groupDef,
init: init,
createGroup: createGroup,
ungroup: ungroup,
addToGroup: addToGroup,
removeFromGroup: removeFromGroup,
getNodes: getNodes,
contains: groupContains
}

View File

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