diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json index 621fdaf41..c50508389 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json +++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json @@ -84,6 +84,8 @@ "alt-a t": "core:align-selection-to-top", "alt-a b": "core:align-selection-to-bottom", "alt-a m": "core:align-selection-to-middle", - "alt-a c": "core:align-selection-to-center" + "alt-a c": "core:align-selection-to-center", + "alt-a h": "core:distribute-selection-horizontally", + "alt-a v": "core:distribute-selection-vertically" } } 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 367e173fd..ede8d3442 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 @@ -531,6 +531,165 @@ RED.view.tools = (function() { } + function distributeSelection(direction) { + var selection = RED.view.selection(); + + if (selection.nodes && selection.nodes.length > 2) { + var changedNodes = []; + var bounds = { + minX: Number.MAX_SAFE_INTEGER, + minY: Number.MAX_SAFE_INTEGER, + maxX: Number.MIN_SAFE_INTEGER, + maxY: Number.MIN_SAFE_INTEGER + } + var startAnchors = []; + var endAnchors = []; + + selection.nodes.forEach(function(n) { + var nx,ny; + if (n.type === "group") { + nx = n.x + n.w/2; + ny = n.y + n.h/2; + } else { + nx = n.x; + ny = n.y; + } + if (direction === "h") { + if (nx < bounds.minX) { + startAnchors = []; + bounds.minX = nx; + } + if (nx === bounds.minX) { + startAnchors.push(n); + } + if (nx > bounds.maxX) { + endAnchors = []; + bounds.maxX = nx; + } + if (nx === bounds.maxX) { + endAnchors.push(n); + } + } else { + if (ny < bounds.minY) { + startAnchors = []; + bounds.minY = ny; + } + if (ny === bounds.minY) { + startAnchors.push(n); + } + if (ny > bounds.maxY) { + endAnchors = []; + bounds.maxY = ny; + } + if (ny === bounds.maxY) { + endAnchors.push(n); + } + } + }); + + var startAnchor = startAnchors[0]; + var endAnchor = endAnchors[0]; + + var nodeSpace = 0; + var nodesToMove = selection.nodes.filter(function(n) { + if (n.id !== startAnchor.id && n.id !== endAnchor.id) { + nodeSpace += direction === 'h'?n.w:n.h; + return true; + } + return false; + }).sort(function(A,B) { + if (direction === 'h') { + return A.x - B.x + } else { + return A.y - B.y + } + }) + + var saX = startAnchor.x + startAnchor.w/2; + var saY = startAnchor.y + startAnchor.h/2; + if (startAnchor.type === "group") { + saX = startAnchor.x + startAnchor.w; + saY = startAnchor.y + startAnchor.h; + } + var eaX = endAnchor.x; + var eaY = endAnchor.y; + if (endAnchor.type !== "group") { + eaX -= endAnchor.w/2; + eaY -= endAnchor.h/2; + } + var spaceToFill = direction === 'h'?(eaX - saX - nodeSpace): (eaY - saY - nodeSpace); + var spaceBetweenNodes = spaceToFill / (nodesToMove.length + 1); + + var tx = saX; + var ty = saY; + while(nodesToMove.length > 0) { + if (direction === 'h') { + tx += spaceBetweenNodes; + } else { + ty += spaceBetweenNodes; + } + var nextNode = nodesToMove.shift(); + var isGroup = nextNode.type==="group"; + + var nx = nextNode.x; + var ny = nextNode.y; + if (!isGroup) { + tx += nextNode.w/2; + ty += nextNode.h/2; + } + if ((direction === 'h' && nx !== tx) || (direction === 'v' && ny !== ty)) { + if (!isGroup) { + changedNodes.push({ + n:nextNode, + ox: nextNode.x, + oy: nextNode.y, + moved: nextNode.moved + }); + if (direction === 'h') { + nextNode.x = tx; + } else { + nextNode.y = ty; + } + nextNode.dirty = true; + nextNode.moved = true; + } else { + var groupNodes = RED.group.getNodes(nextNode, true); + var deltaX = direction === 'h'? nx - tx : 0; + var deltaY = direction === 'v'? ny - ty : 0; + groupNodes.forEach(function(gn) { + if (gn.type !== "group" ) { + changedNodes.push({ + n:gn, + ox: gn.x, + oy: gn.y, + moved: gn.moved + }); + gn.x = gn.x - deltaX; + gn.y = gn.y - deltaY; + gn.dirty = true; + gn.moved = true; + } + }) + } + } + if (isGroup) { + tx += nextNode.w; + ty += nextNode.h; + } else { + tx += nextNode.w/2; + ty += nextNode.h/2; + } + } + + if (changedNodes.length > 0) { + RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()}); + RED.nodes.dirty(true); + RED.view.redraw(true); + } + } + } + + return { init: function() { RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); }) @@ -580,6 +739,8 @@ RED.view.tools = (function() { RED.actions.add("core:align-selection-to-middle", function() { alignSelectionToEdge('middle') }) RED.actions.add("core:align-selection-to-center", function() { alignSelectionToEdge('center') }) + RED.actions.add("core:distribute-selection-horizontally", function() { distributeSelection('h') }) + RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') }) // RED.actions.add("core:add-node", function() { addNode() })