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

Merge pull request #3124 from node-red/raise-lower-nodes

Allow nodes to be raised/lowered in the workspace
This commit is contained in:
Nick O'Leary 2021-09-07 12:19:56 +01:00 committed by GitHub
commit 8970fe412d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 482 additions and 127 deletions

View File

@ -125,7 +125,11 @@
"alignMiddle":"Align to middle", "alignMiddle":"Align to middle",
"alignBottom":"Align to bottom", "alignBottom":"Align to bottom",
"distributeHorizontally":"Distribute horizontally", "distributeHorizontally":"Distribute horizontally",
"distributeVertically":"Distribute vertically" "distributeVertically":"Distribute vertically",
"moveToBack":"Move to back",
"moveToFront":"Move to front",
"moveBackwards":"Move backwards",
"moveForwards":"Move forwards"
} }
}, },
"actions": { "actions": {

View File

@ -558,11 +558,22 @@ RED.history = (function() {
} else if (ev.t == "reorder") { } else if (ev.t == "reorder") {
inverseEv = { inverseEv = {
t: 'reorder', t: 'reorder',
order: RED.nodes.getWorkspaceOrder(),
dirty: RED.nodes.dirty() dirty: RED.nodes.dirty()
}; };
if (ev.order) { if (ev.workspaces) {
RED.workspaces.order(ev.order); inverseEv.workspaces = {
from: ev.workspaces.to,
to: ev.workspaces.from
}
RED.workspaces.order(ev.workspaces.from);
}
if (ev.nodes) {
inverseEv.nodes = {
z: ev.nodes.z,
from: ev.nodes.to,
to: ev.nodes.from
}
RED.nodes.setNodeOrder(ev.nodes.z,ev.nodes.from);
} }
} else if (ev.t == "createGroup") { } else if (ev.t == "createGroup") {
inverseEv = { inverseEv = {

View File

@ -16,8 +16,6 @@
RED.nodes = (function() { RED.nodes = (function() {
var node_defs = {}; var node_defs = {};
var nodes = {};
var nodeTabMap = {};
var linkTabMap = {}; var linkTabMap = {};
var configNodes = {}; var configNodes = {};
@ -41,6 +39,7 @@ RED.nodes = (function() {
RED.events.emit("workspace:dirty",{dirty:dirty}); RED.events.emit("workspace:dirty",{dirty:dirty});
} }
// The registry holds information about all node types.
var registry = (function() { var registry = (function() {
var moduleList = {}; var moduleList = {};
var nodeList = []; var nodeList = [];
@ -209,6 +208,279 @@ RED.nodes = (function() {
return exports; return exports;
})(); })();
// allNodes holds information about the Flow nodes.
var allNodes = (function() {
var nodes = {};
var tabMap = {};
var api = {
addTab: function(id) {
tabMap[id] = [];
},
hasTab: function(z) {
return tabMap.hasOwnProperty(z)
},
removeTab: function(id) {
delete tabMap[id];
},
addNode: function(n) {
nodes[n.id] = n;
if (tabMap.hasOwnProperty(n.z)) {
tabMap[n.z].push(n);
} else {
console.warn("Node added to unknown tab/subflow:",n);
tabMap["_"] = tabMap["_"] || [];
tabMap["_"].push(n);
}
},
removeNode: function(n) {
delete nodes[n.id]
if (tabMap.hasOwnProperty(n.z)) {
var i = tabMap[n.z].indexOf(n);
if (i > -1) {
tabMap[n.z].splice(i,1);
}
}
},
hasNode: function(id) {
return nodes.hasOwnProperty(id);
},
getNode: function(id) {
return nodes[id]
},
moveNode: function(n, newZ) {
api.removeNode(n);
tabMap[newZ] = tabMap[newZ] || [];
tabMap[newZ].push(n);
},
moveNodesForwards: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var moved = new Set();
for (var i = tabNodes.length-1; i >= 0; i--) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i < tabNodes.length-1 && !moved.has(tabNodes[i+1])) {
// Remove from current position
tabNodes.splice(i,1);
// Add it back one position higher
tabNodes.splice(i+1,0,n);
n._reordered = true;
result.push(n);
}
toMove.delete(n);
moved.add(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
},
moveNodesBackwards: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var moved = new Set();
for (var i = 0; i < tabNodes.length; i++) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i > 0 && !moved.has(tabNodes[i-1])) {
// Remove from current position
tabNodes.splice(i,1);
// Add it back one position lower
tabNodes.splice(i-1,0,n);
n._reordered = true;
result.push(n);
}
toMove.delete(n);
moved.add(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
},
moveNodesToFront: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var target = tabNodes.length-1;
for (var i = tabNodes.length-1; i >= 0; i--) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i < target) {
// Remove from current position
tabNodes.splice(i,1);
tabNodes.splice(target,0,n);
n._reordered = true;
result.push(n);
}
target--;
toMove.delete(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
},
moveNodesToBack: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var target = 0;
for (var i = 0; i < tabNodes.length; i++) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i > target) {
// Remove from current position
tabNodes.splice(i,1);
// Add it back one position lower
tabNodes.splice(target,0,n);
n._reordered = true;
result.push(n);
}
target++;
toMove.delete(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
},
getNodes: function(z) {
return tabMap[z];
},
clear: function() {
nodes = {};
tabMap = {};
},
eachNode: function(cb) {
var nodeList,i,j;
for (i in subflows) {
if (subflows.hasOwnProperty(i)) {
nodeList = tabMap[i];
for (j = 0; j < nodeList.length; j++) {
if (cb(nodeList[j]) === false) {
return;
}
}
}
}
for (i = 0; i < workspacesOrder.length; i++) {
nodeList = tabMap[workspacesOrder[i]];
for (j = 0; j < nodeList.length; j++) {
if (cb(nodeList[j]) === false) {
return;
}
}
}
// Flow nodes that do not have a valid tab/subflow
if (tabMap["_"]) {
nodeList = tabMap["_"];
for (j = 0; j < nodeList.length; j++) {
if (cb(nodeList[j]) === false) {
return;
}
}
}
},
filterNodes: function(filter) {
var result = [];
var searchSet = null;
var doZFilter = false;
if (filter.hasOwnProperty("z")) {
if (tabMap.hasOwnProperty(filter.z)) {
searchSet = tabMap[filter.z];
} else {
doZFilter = true;
}
}
if (searchSet === null) {
searchSet = nodes;
}
for (var n=0;n<searchSet.length;n++) {
var node = searchSet[n];
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
continue;
}
if (doZFilter && node.z !== filter.z) {
continue;
}
result.push(node);
}
return result;
},
getNodeOrder: function(z) {
return tabMap[z].map(function(n) { return n.id })
},
setNodeOrder: function(z, order) {
var orderMap = {};
order.forEach(function(id,i) {
orderMap[id] = i;
})
tabMap[z].sort(function(A,B) {
A._reordered = true;
B._reordered = true;
return orderMap[A.id] - orderMap[B.id];
})
}
}
return api;
})()
function getID() { function getID() {
var bytes = []; var bytes = [];
for (var i=0;i<8;i++) { for (var i=0;i<8;i++) {
@ -294,15 +566,10 @@ RED.nodes = (function() {
}); });
n.i = nextId+1; n.i = nextId+1;
} }
nodes[n.id] = n; allNodes.addNode(n);
if (!nodeLinks[n.id]) { if (!nodeLinks[n.id]) {
nodeLinks[n.id] = {in:[],out:[]}; nodeLinks[n.id] = {in:[],out:[]};
} }
if (nodeTabMap[n.z]) {
nodeTabMap[n.z][n.id] = n;
} else {
console.warn("Node added to unknown tab/subflow:",n);
}
} }
RED.events.emit('nodes:add',n); RED.events.emit('nodes:add',n);
} }
@ -330,10 +597,8 @@ RED.nodes = (function() {
function getNode(id) { function getNode(id) {
if (id in configNodes) { if (id in configNodes) {
return configNodes[id]; return configNodes[id];
} else if (id in nodes) {
return nodes[id];
} }
return null; return allNodes.getNode(id);
} }
function removeNode(id) { function removeNode(id) {
@ -345,13 +610,10 @@ RED.nodes = (function() {
delete configNodes[id]; delete configNodes[id];
RED.events.emit('nodes:remove',node); RED.events.emit('nodes:remove',node);
RED.workspaces.refresh(); RED.workspaces.refresh();
} else if (id in nodes) { } else if (allNodes.hasNode(id)) {
node = nodes[id]; node = allNodes.getNode(id);
delete nodes[id] allNodes.removeNode(node);
delete nodeLinks[id]; delete nodeLinks[id];
if (nodeTabMap[node.z]) {
delete nodeTabMap[node.z][node.id];
}
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); }); removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
removedLinks.forEach(removeLink); removedLinks.forEach(removeLink);
var updatedConfigNode = false; var updatedConfigNode = false;
@ -409,18 +671,32 @@ RED.nodes = (function() {
return {links:removedLinks,nodes:removedNodes}; return {links:removedLinks,nodes:removedNodes};
} }
function moveNodesForwards(nodes) {
return allNodes.moveNodesForwards(nodes);
}
function moveNodesBackwards(nodes) {
return allNodes.moveNodesBackwards(nodes);
}
function moveNodesToFront(nodes) {
return allNodes.moveNodesToFront(nodes);
}
function moveNodesToBack(nodes) {
return allNodes.moveNodesToBack(nodes);
}
function getNodeOrder(z) {
return allNodes.getNodeOrder(z);
}
function setNodeOrder(z, order) {
allNodes.setNodeOrder(z,order);
}
function moveNodeToTab(node, z) { function moveNodeToTab(node, z) {
if (node.type === "group") { if (node.type === "group") {
moveGroupToTab(node,z); moveGroupToTab(node,z);
return; return;
} }
if (nodeTabMap[node.z]) { allNodes.moveNode(node,z);
delete nodeTabMap[node.z][node.id];
}
if (!nodeTabMap[z]) {
nodeTabMap[z] = {};
}
nodeTabMap[z][node.id] = node;
var nl = nodeLinks[node.id]; var nl = nodeLinks[node.id];
if (nl) { if (nl) {
nl.in.forEach(function(l) { nl.in.forEach(function(l) {
@ -482,7 +758,7 @@ RED.nodes = (function() {
function addWorkspace(ws,targetIndex) { function addWorkspace(ws,targetIndex) {
workspaces[ws.id] = ws; workspaces[ws.id] = ws;
nodeTabMap[ws.id] = {}; allNodes.addTab(ws.id);
linkTabMap[ws.id] = []; linkTabMap[ws.id] = [];
ws._def = RED.nodes.getType('tab'); ws._def = RED.nodes.getType('tab');
@ -506,19 +782,14 @@ RED.nodes = (function() {
var removedGroups = []; var removedGroups = [];
if (ws) { if (ws) {
delete workspaces[id]; delete workspaces[id];
delete nodeTabMap[id]; allNodes.removeTab(id);
delete linkTabMap[id]; delete linkTabMap[id];
workspacesOrder.splice(workspacesOrder.indexOf(id),1); workspacesOrder.splice(workspacesOrder.indexOf(id),1);
var i; var i;
var node; var node;
// TODO: this should use nodeTabMap
for (i in nodes) { if (allNodes.hasTab(id)) {
if (nodes.hasOwnProperty(i)) { removedNodes = allNodes.getNodes(id).slice()
node = nodes[i];
if (node.z == id) {
removedNodes.push(node);
}
}
} }
for (i in configNodes) { for (i in configNodes) {
if (configNodes.hasOwnProperty(i)) { if (configNodes.hasOwnProperty(i)) {
@ -572,7 +843,7 @@ RED.nodes = (function() {
sf.name = subflowName; sf.name = subflowName;
} }
subflows[sf.id] = sf; subflows[sf.id] = sf;
nodeTabMap[sf.id] = {}; allNodes.addTab(sf.id);
linkTabMap[sf.id] = []; linkTabMap[sf.id] = [];
RED.nodes.registerType("subflow:"+sf.id, { RED.nodes.registerType("subflow:"+sf.id, {
@ -618,17 +889,16 @@ RED.nodes = (function() {
function removeSubflow(sf) { function removeSubflow(sf) {
if (subflows[sf.id]) { if (subflows[sf.id]) {
delete subflows[sf.id]; delete subflows[sf.id];
delete nodeTabMap[sf.id]; allNodes.removeTab(sf.id);
registry.removeNodeType("subflow:"+sf.id); registry.removeNodeType("subflow:"+sf.id);
RED.events.emit("subflows:remove",sf); RED.events.emit("subflows:remove",sf);
} }
} }
function subflowContains(sfid,nodeid) { function subflowContains(sfid,nodeid) {
for (var i in nodes) { var sfNodes = allNodes.getNodes(sfid);
if (nodes.hasOwnProperty(i)) { for (var i = 0; i<sfNodes.length; i++) {
var node = nodes[i]; var node = sfNodes[i];
if (node.z === sfid) {
var m = /^subflow:(.+)$/.exec(node.type); var m = /^subflow:(.+)$/.exec(node.type);
if (m) { if (m) {
if (m[1] === nodeid) { if (m[1] === nodeid) {
@ -641,8 +911,6 @@ RED.nodes = (function() {
} }
} }
} }
}
}
return false; return false;
} }
@ -934,11 +1202,15 @@ RED.nodes = (function() {
function createExportableSubflow(id) { function createExportableSubflow(id) {
var sf = getSubflow(id); var sf = getSubflow(id);
var nodeSet = [sf]; var nodeSet;
var sfNodeIds = Object.keys(nodeTabMap[sf.id]||{}); var sfNodes = allNodes.getNodes(sf.id);
for (var i=0, l=sfNodeIds.length; i<l; i++) { if (sfNodes) {
nodeSet.push(nodeTabMap[sf.id][sfNodeIds[i]]); nodeSet = sfNodes.slice();
nodeSet.unshift(sf);
} else {
nodeSet = [sf];
} }
console.log(nodeSet);
return createExportableNodeSet(nodeSet); return createExportableNodeSet(nodeSet);
} }
/** /**
@ -965,12 +1237,9 @@ RED.nodes = (function() {
if (!exportedSubflows[subflowId]) { if (!exportedSubflows[subflowId]) {
exportedSubflows[subflowId] = true; exportedSubflows[subflowId] = true;
var subflow = getSubflow(subflowId); var subflow = getSubflow(subflowId);
var subflowSet = [subflow]; var subflowSet = allNodes.getNodes(subflowId).slice();
RED.nodes.eachNode(function(n) { subflowSet.unshift(subflow);
if (n.z == subflowId) {
subflowSet.push(n);
}
});
RED.nodes.eachConfig(function(n) { RED.nodes.eachConfig(function(n) {
if (n.z == subflowId) { if (n.z == subflowId) {
subflowSet.push(n); subflowSet.push(n);
@ -1048,11 +1317,9 @@ RED.nodes = (function() {
nns.push(convertNode(configNodes[i], opts)); nns.push(convertNode(configNodes[i], opts));
} }
} }
for (i in nodes) { RED.nodes.eachNode(function(n) {
if (nodes.hasOwnProperty(i)) { nns.push(convertNode(n, opts));
nns.push(convertNode(nodes[i], opts)); })
}
}
return nns; return nns;
} }
@ -1149,7 +1416,7 @@ RED.nodes = (function() {
var nodeZ = n.z || "__global__"; var nodeZ = n.z || "__global__";
imported.zMap[nodeZ] = imported.zMap[nodeZ] || []; imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
imported.zMap[nodeZ].push(n) imported.zMap[nodeZ].push(n)
if (nodes[n.id] || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) { if (allNodes.hasNode(n.id) || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) {
imported.conflicted[n.id] = n; imported.conflicted[n.id] = n;
} }
}) })
@ -1157,7 +1424,6 @@ RED.nodes = (function() {
} }
/** /**
* Replace the provided nodes. * Replace the provided nodes.
* This must contain complete Subflow defs or complete Flow Tabs. * This must contain complete Subflow defs or complete Flow Tabs.
@ -1316,7 +1582,7 @@ RED.nodes = (function() {
if (!options.generateIds) { if (!options.generateIds) {
if (!options.importMap[id]) { if (!options.importMap[id]) {
// No conflict resolution for this node // No conflict resolution for this node
var existing = nodes[id] || configNodes[id] || workspaces[id] || subflows[id] || groups[id]; var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id];
if (existing) { if (existing) {
existingNodes.push({existing:existing, imported:n}); existingNodes.push({existing:existing, imported:n});
} }
@ -1979,32 +2245,9 @@ RED.nodes = (function() {
// TODO: supports filter.z|type // TODO: supports filter.z|type
function filterNodes(filter) { function filterNodes(filter) {
var result = []; return allNodes.filterNodes(filter);
var searchSet = null;
var doZFilter = false;
if (filter.hasOwnProperty("z")) {
if (nodeTabMap.hasOwnProperty(filter.z)) {
searchSet = Object.keys(nodeTabMap[filter.z]);
} else {
doZFilter = true;
}
}
if (searchSet === null) {
searchSet = Object.keys(nodes);
} }
for (var n=0;n<searchSet.length;n++) {
var node = nodes[searchSet[n]];
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
continue;
}
if (doZFilter && node.z !== filter.z) {
continue;
}
result.push(node);
}
return result;
}
function filterLinks(filter) { function filterLinks(filter) {
var result = []; var result = [];
var candidateLinks = []; var candidateLinks = [];
@ -2092,9 +2335,8 @@ RED.nodes = (function() {
} }
function clear() { function clear() {
nodes = {}; allNodes.clear();
links = []; links = [];
nodeTabMap = {};
linkTabMap = {}; linkTabMap = {};
nodeLinks = {}; nodeLinks = {};
configNodes = {}; configNodes = {};
@ -2186,10 +2428,7 @@ RED.nodes = (function() {
if (configNodes.hasOwnProperty(n.id)) { if (configNodes.hasOwnProperty(n.id)) {
delete configNodes[n.id]; delete configNodes[n.id];
} else { } else {
delete nodes[n.id]; allNodes.removeNode(n);
if (nodeTabMap[n.z]) {
delete nodeTabMap[n.z][n.id];
}
} }
reimportList.push(convertNode(n)); reimportList.push(convertNode(n));
RED.events.emit('nodes:remove',n); RED.events.emit('nodes:remove',n);
@ -2246,6 +2485,13 @@ RED.nodes = (function() {
remove: removeNode, remove: removeNode,
clear: clear, clear: clear,
moveNodesForwards: moveNodesForwards,
moveNodesBackwards: moveNodesBackwards,
moveNodesToFront: moveNodesToFront,
moveNodesToBack: moveNodesToBack,
getNodeOrder: getNodeOrder,
setNodeOrder: setNodeOrder,
moveNodeToTab: moveNodeToTab, moveNodeToTab: moveNodeToTab,
addLink: addLink, addLink: addLink,
@ -2265,16 +2511,10 @@ RED.nodes = (function() {
addGroup: addGroup, addGroup: addGroup,
removeGroup: removeGroup, removeGroup: removeGroup,
group: function(id) { return groups[id] }, group: function(id) { return groups[id] },
groups: function(z) { return groupsByZ[z]||[] }, groups: function(z) { return groupsByZ[z]?groupsByZ[z].slice():[] },
eachNode: function(cb) { eachNode: function(cb) {
for (var id in nodes) { allNodes.eachNode(cb);
if (nodes.hasOwnProperty(id)) {
if (cb(nodes[id]) === false) {
break;
}
}
}
}, },
eachLink: function(cb) { eachLink: function(cb) {
for (var l=0;l<links.length;l++) { for (var l=0;l<links.length;l++) {

View File

@ -584,16 +584,21 @@ var RED = (function() {
]}); ]});
menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [ menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [
{id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"}, {id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
{id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"}, {id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
{id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"}, {id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"},
null, null,
{id: "menu-item-view-tools-align-top", label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"}, {id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"},
{id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"}, {id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"},
{id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"}, {id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"},
null, null,
{id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"}, {id: "menu-item-view-tools-align-top", label:RED._("menu.label.alignTop"), disabled: true, onselect: "core:align-selection-to-top"},
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"} {id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), disabled: true, onselect: "core:align-selection-to-middle"},
{id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), disabled: true, onselect: "core:align-selection-to-bottom"},
null,
{id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), disabled: true, onselect: "core:distribute-selection-horizontally"},
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"}
]}); ]});
menuOptions.push(null); menuOptions.push(null);

View File

@ -311,6 +311,7 @@ RED.sidebar.info.outliner = (function() {
RED.events.on("nodes:add",onNodeAdd); RED.events.on("nodes:add",onNodeAdd);
RED.events.on("nodes:remove",onObjectRemove); RED.events.on("nodes:remove",onObjectRemove);
RED.events.on("nodes:change",onNodeChange); RED.events.on("nodes:change",onNodeChange);
// RED.events.on("nodes:reorder",onNodesReorder);
RED.events.on("groups:add",onNodeAdd); RED.events.on("groups:add",onNodeAdd);
RED.events.on("groups:remove",onObjectRemove); RED.events.on("groups:remove",onObjectRemove);
@ -369,6 +370,21 @@ RED.sidebar.info.outliner = (function() {
return indexMap[A.id] - indexMap[B.id] return indexMap[A.id] - indexMap[B.id]
}) })
} }
// function onNodesReorder(event) {
// //
// var nodes = RED.nodes.getNodeOrder(event.z);
// var indexMap = {};
// nodes.forEach(function(id,index) {
// indexMap[id] = index;
// })
// var existingObject = objects[event.z];
// existingObject.treeList.sortChildren(function(A,B) {
// if (A.children && !B.children) { return -1 }
// if (!A.children && B.children) { return 1 }
// if (A.children && B.children) { return -1 }
// return indexMap[A.id] - indexMap[B.id]
// })
// }
function onSubflowAdd(sf) { function onSubflowAdd(sf) {
objects[sf.id] = { objects[sf.id] = {
id: sf.id, id: sf.id,

View File

@ -689,6 +689,41 @@ RED.view.tools = (function() {
} }
} }
function reorderSelection(dir) {
var selection = RED.view.selection();
if (selection.nodes) {
var nodesToMove = [];
selection.nodes.forEach(function(n) {
if (n.type === "group") {
nodesToMove = nodesToMove.concat(RED.group.getNodes(n, true).filter(function(n) {
return n.type !== "group";
}))
} else if (n.type !== "subflow"){
nodesToMove.push(n);
}
})
if (nodesToMove.length > 0) {
var z = nodesToMove[0].z;
var existingOrder = RED.nodes.getNodeOrder(z);
var movedNodes;
if (dir === "forwards") {
movedNodes = RED.nodes.moveNodesForwards(nodesToMove);
} else if (dir === "backwards") {
movedNodes = RED.nodes.moveNodesBackwards(nodesToMove);
} else if (dir === "front") {
movedNodes = RED.nodes.moveNodesToFront(nodesToMove);
} else if (dir === "back") {
movedNodes = RED.nodes.moveNodesToBack(nodesToMove);
}
if (movedNodes.length > 0) {
var newOrder = RED.nodes.getNodeOrder(z);
RED.history.push({t:"reorder",nodes:{z:z,from:existingOrder,to:newOrder},dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
return { return {
init: function() { init: function() {
@ -710,6 +745,12 @@ RED.view.tools = (function() {
RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);}); RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);});
RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);}); RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);});
RED.actions.add("core:move-selection-forwards", function() { reorderSelection('forwards') })
RED.actions.add("core:move-selection-backwards", function() { reorderSelection('backwards') })
RED.actions.add("core:move-selection-to-front", function() { reorderSelection('front') })
RED.actions.add("core:move-selection-to-back", function() { reorderSelection('back') })
RED.actions.add("core:step-selection-up", function() { moveSelection(0,-RED.view.gridSize());}); RED.actions.add("core:step-selection-up", function() { moveSelection(0,-RED.view.gridSize());});
RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);}); RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);});
RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());}); RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());});
@ -743,6 +784,7 @@ RED.view.tools = (function() {
RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') }) RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') })
// RED.actions.add("core:add-node", function() { addNode() }) // RED.actions.add("core:add-node", function() { addNode() })
}, },
/** /**

View File

@ -504,9 +504,23 @@ RED.view = (function() {
RED.events.on("view:selection-changed", function(selection) { RED.events.on("view:selection-changed", function(selection) {
var hasSelection = (selection.nodes && selection.nodes.length > 0); var hasSelection = (selection.nodes && selection.nodes.length > 0);
var hasMultipleSelection = hasSelection && selection.nodes.length > 1;
RED.menu.setDisabled("menu-item-edit-cut",!hasSelection); RED.menu.setDisabled("menu-item-edit-cut",!hasSelection);
RED.menu.setDisabled("menu-item-edit-copy",!hasSelection); RED.menu.setDisabled("menu-item-edit-copy",!hasSelection);
RED.menu.setDisabled("menu-item-edit-select-connected",!hasSelection); RED.menu.setDisabled("menu-item-edit-select-connected",!hasSelection);
RED.menu.setDisabled("menu-item-view-tools-move-to-back",!hasSelection);
RED.menu.setDisabled("menu-item-view-tools-move-to-front",!hasSelection);
RED.menu.setDisabled("menu-item-view-tools-move-backwards",!hasSelection);
RED.menu.setDisabled("menu-item-view-tools-move-forwards",!hasSelection);
RED.menu.setDisabled("menu-item-view-tools-align-left",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-align-center",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-align-right",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-align-top",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-align-middle",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-align-bottom",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally",!hasMultipleSelection);
RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally",!hasMultipleSelection);
}) })
RED.actions.add("core:delete-selection",deleteSelection); RED.actions.add("core:delete-selection",deleteSelection);
@ -663,14 +677,17 @@ RED.view = (function() {
var activeWorkspace = RED.workspaces.active(); var activeWorkspace = RED.workspaces.active();
activeNodes = RED.nodes.filterNodes({z:activeWorkspace}); activeNodes = RED.nodes.filterNodes({z:activeWorkspace});
activeNodes.forEach(function(n,i) {
n._index = i;
})
activeLinks = RED.nodes.filterLinks({ activeLinks = RED.nodes.filterLinks({
source:{z:activeWorkspace}, source:{z:activeWorkspace},
target:{z:activeWorkspace} target:{z:activeWorkspace}
}); });
activeGroups = RED.nodes.groups(activeWorkspace)||[]; activeGroups = RED.nodes.groups(activeWorkspace)||[];
activeGroups.forEach(function(g) { activeGroups.forEach(function(g,i) {
g._index = i;
if (g.g) { if (g.g) {
g._root = g.g; g._root = g.g;
g._depth = 1; g._depth = 1;
@ -703,7 +720,8 @@ RED.view = (function() {
if (a._root === b._root) { if (a._root === b._root) {
return a._depth - b._depth; return a._depth - b._depth;
} else { } else {
return a._root.localeCompare(b._root); // return a._root.localeCompare(b._root);
return a._index - b._index;
} }
}); });
@ -712,7 +730,8 @@ RED.view = (function() {
if (a._root === b._root) { if (a._root === b._root) {
return a._depth - b._depth; return a._depth - b._depth;
} else { } else {
return a._root.localeCompare(b._root); return a._index - b._index;
// return a._root.localeCompare(b._root);
} }
}) })
} }
@ -3816,7 +3835,6 @@ RED.view = (function() {
.attr("class", "red-ui-flow-node red-ui-flow-node-group") .attr("class", "red-ui-flow-node red-ui-flow-node-group")
.classed("red-ui-flow-subflow", activeSubflow != null); .classed("red-ui-flow-subflow", activeSubflow != null);
nodeEnter.each(function(d,i) { nodeEnter.each(function(d,i) {
this.__outputs__ = []; this.__outputs__ = [];
this.__inputs__ = []; this.__inputs__ = [];
@ -3962,7 +3980,12 @@ RED.view = (function() {
RED.hooks.trigger("viewAddNode",{node:d,el:this}) RED.hooks.trigger("viewAddNode",{node:d,el:this})
}); });
var nodesReordered = false;
node.each(function(d,i) { node.each(function(d,i) {
if (d._reordered) {
nodesReordered = true;
delete d._reordered;
}
if (d.dirty) { if (d.dirty) {
var self = this; var self = this;
var thisNode = d3.select(this); var thisNode = d3.select(this);
@ -4270,6 +4293,13 @@ RED.view = (function() {
RED.hooks.trigger("viewRedrawNode",{node:d,el:this}) RED.hooks.trigger("viewRedrawNode",{node:d,el:this})
}); });
if (nodesReordered) {
node.sort(function(a,b) {
return a._index - b._index;
})
}
var link = linkLayer.selectAll(".red-ui-flow-link").data( var link = linkLayer.selectAll(".red-ui-flow-link").data(
activeLinks, activeLinks,
function(d) { function(d) {
@ -4508,7 +4538,7 @@ RED.view = (function() {
if (a._root === b._root) { if (a._root === b._root) {
return a._depth - b._depth; return a._depth - b._depth;
} else { } else {
return a._root.localeCompare(b._root); return a._index - b._index;
} }
}) })
} }

View File

@ -293,7 +293,14 @@ RED.workspaces = (function() {
} }
}, },
onreorder: function(oldOrder, newOrder) { onreorder: function(oldOrder, newOrder) {
RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()}); RED.history.push({
t:'reorder',
workspaces: {
from:oldOrder,
to:newOrder
},
dirty:RED.nodes.dirty()
});
RED.nodes.dirty(true); RED.nodes.dirty(true);
setWorkspaceOrder(newOrder); setWorkspaceOrder(newOrder);
}, },