Skip to content

Commit 42fc7cd

Browse files
authored
Merge pull request #388 from M1kep/alignment-no-whitespace-changes
Add ability to align selected nodes
2 parents 802dfaa + e466b5e commit 42fc7cd

2 files changed

Lines changed: 130 additions & 1 deletion

File tree

src/litegraph.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10294,6 +10294,119 @@ LGraphNode.prototype.executeAction = function(action)
1029410294
canvas.graph.add(group);
1029510295
};
1029610296

10297+
/**
10298+
* Determines the furthest nodes in each direction
10299+
* @param nodes {LGraphNode[]} the nodes to from which boundary nodes will be extracted
10300+
* @return {{left: LGraphNode, top: LGraphNode, right: LGraphNode, bottom: LGraphNode}}
10301+
*/
10302+
LGraphCanvas.getBoundaryNodes = function(nodes) {
10303+
let top = null;
10304+
let right = null;
10305+
let bottom = null;
10306+
let left = null;
10307+
for (const nID in nodes) {
10308+
const node = nodes[nID];
10309+
const [x, y] = node.pos;
10310+
const [width, height] = node.size;
10311+
10312+
if (top === null || y < top.pos[1]) {
10313+
top = node;
10314+
}
10315+
if (right === null || x + width > right.pos[0] + right.size[0]) {
10316+
right = node;
10317+
}
10318+
if (bottom === null || y + height > bottom.pos[1] + bottom.size[1]) {
10319+
bottom = node;
10320+
}
10321+
if (left === null || x < left.pos[0]) {
10322+
left = node;
10323+
}
10324+
}
10325+
10326+
return {
10327+
"top": top,
10328+
"right": right,
10329+
"bottom": bottom,
10330+
"left": left
10331+
};
10332+
}
10333+
/**
10334+
* Determines the furthest nodes in each direction for the currently selected nodes
10335+
* @return {{left: LGraphNode, top: LGraphNode, right: LGraphNode, bottom: LGraphNode}}
10336+
*/
10337+
LGraphCanvas.prototype.boundaryNodesForSelection = function() {
10338+
return LGraphCanvas.getBoundaryNodes(Object.values(this.selected_nodes));
10339+
}
10340+
10341+
/**
10342+
*
10343+
* @param {LGraphNode[]} nodes a list of nodes
10344+
* @param {"top"|"bottom"|"left"|"right"} direction Direction to align the nodes
10345+
* @param {LGraphNode?} align_to Node to align to (if null, align to the furthest node in the given direction)
10346+
*/
10347+
LGraphCanvas.alignNodes = function (nodes, direction, align_to) {
10348+
if (!nodes) {
10349+
return;
10350+
}
10351+
10352+
const canvas = LGraphCanvas.active_canvas;
10353+
let boundaryNodes = []
10354+
if (align_to === undefined) {
10355+
boundaryNodes = LGraphCanvas.getBoundaryNodes(nodes)
10356+
} else {
10357+
boundaryNodes = {
10358+
"top": align_to,
10359+
"right": align_to,
10360+
"bottom": align_to,
10361+
"left": align_to
10362+
}
10363+
}
10364+
10365+
for (const [_, node] of Object.entries(canvas.selected_nodes)) {
10366+
switch (direction) {
10367+
case "right":
10368+
node.pos[0] = boundaryNodes["right"].pos[0] + boundaryNodes["right"].size[0] - node.size[0];
10369+
break;
10370+
case "left":
10371+
node.pos[0] = boundaryNodes["left"].pos[0];
10372+
break;
10373+
case "top":
10374+
node.pos[1] = boundaryNodes["top"].pos[1];
10375+
break;
10376+
case "bottom":
10377+
node.pos[1] = boundaryNodes["bottom"].pos[1] + boundaryNodes["bottom"].size[1] - node.size[1];
10378+
break;
10379+
}
10380+
}
10381+
10382+
canvas.dirty_canvas = true;
10383+
canvas.dirty_bgcanvas = true;
10384+
};
10385+
10386+
LGraphCanvas.onNodeAlign = function(value, options, event, prev_menu, node) {
10387+
new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], {
10388+
event: event,
10389+
callback: inner_clicked,
10390+
parentMenu: prev_menu,
10391+
});
10392+
10393+
function inner_clicked(value) {
10394+
LGraphCanvas.alignNodes(LGraphCanvas.active_canvas.selected_nodes, value.toLowerCase(), node);
10395+
}
10396+
}
10397+
10398+
LGraphCanvas.onGroupAlign = function(value, options, event, prev_menu) {
10399+
new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], {
10400+
event: event,
10401+
callback: inner_clicked,
10402+
parentMenu: prev_menu,
10403+
});
10404+
10405+
function inner_clicked(value) {
10406+
LGraphCanvas.alignNodes(LGraphCanvas.active_canvas.selected_nodes, value.toLowerCase());
10407+
}
10408+
}
10409+
1029710410
LGraphCanvas.onMenuAdd = function (node, options, e, prev_menu, callback) {
1029810411

1029910412
var canvas = LGraphCanvas.active_canvas;
@@ -12894,6 +13007,14 @@ LGraphNode.prototype.executeAction = function(action)
1289413007
options.push({ content: "Options", callback: that.showShowGraphOptionsPanel });
1289513008
}*/
1289613009

13010+
if (Object.keys(this.selected_nodes).length > 1) {
13011+
options.push({
13012+
content: "Align",
13013+
has_submenu: true,
13014+
callback: LGraphCanvas.onGroupAlign,
13015+
})
13016+
}
13017+
1289713018
if (this._graph_stack && this._graph_stack.length > 0) {
1289813019
options.push(null, {
1289913020
content: "Close subgraph",
@@ -13008,6 +13129,14 @@ LGraphNode.prototype.executeAction = function(action)
1300813129
callback: LGraphCanvas.onMenuNodeToSubgraph
1300913130
});
1301013131

13132+
if (Object.keys(this.selected_nodes).length > 1) {
13133+
options.push({
13134+
content: "Align Selected To",
13135+
has_submenu: true,
13136+
callback: LGraphCanvas.onNodeAlign,
13137+
})
13138+
}
13139+
1301113140
options.push(null, {
1301213141
content: "Remove",
1301313142
disabled: !(node.removable !== false && !node.block_delete ),

utils/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ app.use('/external', express.static('external'))
77
app.use('/editor', express.static('editor'))
88
app.use('/', express.static('editor'))
99

10-
app.listen(8000, () => console.log('Example app listening on port 8000!'))
10+
app.listen(8000, () => console.log('Example app listening on http://127.0.0.1:8000!'))

0 commit comments

Comments
 (0)