From badf134f123ceb60ed1aa9a86528e1d3fcfb53c6 Mon Sep 17 00:00:00 2001 From: Michael Poutre Date: Sun, 30 Apr 2023 23:26:07 -0700 Subject: [PATCH 1/4] feat: Add alignment for selected node --- src/litegraph.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/litegraph.js b/src/litegraph.js index a77e0d12e..e61b781c7 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -10294,6 +10294,96 @@ LGraphNode.prototype.executeAction = function(action) canvas.graph.add(group); }; + /** + * Determines the furthest nodes in each direction + * @param nodes {LGraphNode[]} the nodes to from which boundary nodes will be extracted + * @return {{left: LGraphNode, top: LGraphNode, right: LGraphNode, bottom: LGraphNode}} + */ + LGraphCanvas.getBoundaryNodes = function(nodes) { + let top = null; + let right = null; + let bottom = null; + let left = null; + for (const nID in nodes) { + const node = nodes[nID]; + const [x, y] = node.pos; + const [width, height] = node.size; + + if (top === null || y < top.pos[1]) { + top = node; + } + if (right === null || x + width > right.pos[0] + right.size[0]) { + right = node; + } + if (bottom === null || y + height > bottom.pos[1] + bottom.size[1]) { + bottom = node; + } + if (left === null || x < left.pos[0]) { + left = node; + } + } + + return { + "top": top, + "right": right, + "bottom": bottom, + "left": left + }; + } + /** + * Determines the furthest nodes in each direction for the currently selected nodes + * @return {{left: LGraphNode, top: LGraphNode, right: LGraphNode, bottom: LGraphNode}} + */ + LGraphCanvas.prototype.boundaryNodesForSelection = function() { + return LGraphCanvas.getBoundaryNodes(Object.values(this.selected_nodes)); + } + + /** + * + * @param {LGraphNode[]} nodes a list of nodes + * @param {"top"|"bottom"|"left"|"right"} direction Direction to align the nodes + */ + LGraphCanvas.alignNodes = function (nodes, direction) { + if (!nodes) { + return; + } + + const canvas = LGraphCanvas.active_canvas; + const boundaryNodes = LGraphCanvas.getBoundaryNodes(nodes) + + for (const [_, node] of Object.entries(canvas.selected_nodes)) { + switch (direction) { + case "right": + node.pos[0] = boundaryNodes["right"].pos[0] + boundaryNodes["right"].size[0] - node.size[0]; + break; + case "left": + node.pos[0] = boundaryNodes["left"].pos[0]; + break; + case "top": + node.pos[1] = boundaryNodes["top"].pos[1]; + break; + case "bottom": + node.pos[1] = boundaryNodes["bottom"].pos[1] + boundaryNodes["bottom"].size[1] - node.size[1]; + break; + } + } + + canvas.dirty_canvas = true; + canvas.dirty_bgcanvas = true; + }; + + LGraphCanvas.onGroupAlign = function(value, options, event, prev_menu) { + new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], { + event: event, + callback: inner_clicked, + parentMenu: prev_menu, + }); + + function inner_clicked(value) { + LGraphCanvas.alignNodes(LGraphCanvas.active_canvas.selected_nodes, value.toLowerCase()); + } + } + LGraphCanvas.onMenuAdd = function (node, options, e, prev_menu, callback) { var canvas = LGraphCanvas.active_canvas; @@ -12887,6 +12977,7 @@ LGraphNode.prototype.executeAction = function(action) callback: LGraphCanvas.onMenuAdd }, { content: "Add Group", callback: LGraphCanvas.onGroupAdd }, + { content: "Align", has_submenu: true, callback: LGraphCanvas.onGroupAlign }, //{ content: "Arrange", callback: that.graph.arrange }, //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } ]; From 9f3245d9f0043b87a3d4b8aa111264a228286efc Mon Sep 17 00:00:00 2001 From: Michael Poutre Date: Sun, 30 Apr 2023 22:40:02 -0700 Subject: [PATCH 2/4] refactor: Only show Align options if more than one node is selected --- src/litegraph.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/litegraph.js b/src/litegraph.js index e61b781c7..043df4d88 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -12977,7 +12977,6 @@ LGraphNode.prototype.executeAction = function(action) callback: LGraphCanvas.onMenuAdd }, { content: "Add Group", callback: LGraphCanvas.onGroupAdd }, - { content: "Align", has_submenu: true, callback: LGraphCanvas.onGroupAlign }, //{ content: "Arrange", callback: that.graph.arrange }, //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } ]; @@ -12985,6 +12984,14 @@ LGraphNode.prototype.executeAction = function(action) options.push({ content: "Options", callback: that.showShowGraphOptionsPanel }); }*/ + if (Object.keys(this.selected_nodes).length > 1) { + options.push({ + content: "Align", + has_submenu: true, + callback: LGraphCanvas.onGroupAlign, + }) + } + if (this._graph_stack && this._graph_stack.length > 0) { options.push(null, { content: "Close subgraph", From cfc64dfe5290bf588430d974d8f855b25984d16b Mon Sep 17 00:00:00 2001 From: Michael Poutre Date: Sun, 30 Apr 2023 22:45:53 -0700 Subject: [PATCH 3/4] feat: Target alignment when right clicking a specific node --- src/litegraph.js | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/litegraph.js b/src/litegraph.js index 043df4d88..760d55bf8 100755 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -10342,14 +10342,25 @@ LGraphNode.prototype.executeAction = function(action) * * @param {LGraphNode[]} nodes a list of nodes * @param {"top"|"bottom"|"left"|"right"} direction Direction to align the nodes + * @param {LGraphNode?} align_to Node to align to (if null, align to the furthest node in the given direction) */ - LGraphCanvas.alignNodes = function (nodes, direction) { + LGraphCanvas.alignNodes = function (nodes, direction, align_to) { if (!nodes) { return; } const canvas = LGraphCanvas.active_canvas; - const boundaryNodes = LGraphCanvas.getBoundaryNodes(nodes) + let boundaryNodes = [] + if (align_to === undefined) { + boundaryNodes = LGraphCanvas.getBoundaryNodes(nodes) + } else { + boundaryNodes = { + "top": align_to, + "right": align_to, + "bottom": align_to, + "left": align_to + } + } for (const [_, node] of Object.entries(canvas.selected_nodes)) { switch (direction) { @@ -10372,6 +10383,18 @@ LGraphNode.prototype.executeAction = function(action) canvas.dirty_bgcanvas = true; }; + LGraphCanvas.onNodeAlign = function(value, options, event, prev_menu, node) { + new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], { + event: event, + callback: inner_clicked, + parentMenu: prev_menu, + }); + + function inner_clicked(value) { + LGraphCanvas.alignNodes(LGraphCanvas.active_canvas.selected_nodes, value.toLowerCase(), node); + } + } + LGraphCanvas.onGroupAlign = function(value, options, event, prev_menu) { new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], { event: event, @@ -13106,6 +13129,14 @@ LGraphNode.prototype.executeAction = function(action) callback: LGraphCanvas.onMenuNodeToSubgraph }); + if (Object.keys(this.selected_nodes).length > 1) { + options.push({ + content: "Align Selected To", + has_submenu: true, + callback: LGraphCanvas.onNodeAlign, + }) + } + options.push(null, { content: "Remove", disabled: !(node.removable !== false && !node.block_delete ), From e466b5edc8f76bdb2c3e2cdc0bf6e9fc798d4f52 Mon Sep 17 00:00:00 2001 From: Michael Poutre Date: Sun, 30 Apr 2023 22:47:42 -0700 Subject: [PATCH 4/4] refactor: Update startup message for example app to include URL Allows clicking the URL to launch example app, this also allows some IDEs such as WebStorm to launch integrated debugger far more easily --- utils/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/server.js b/utils/server.js index 9171c7b9d..490522588 100755 --- a/utils/server.js +++ b/utils/server.js @@ -7,4 +7,4 @@ app.use('/external', express.static('external')) app.use('/editor', express.static('editor')) app.use('/', express.static('editor')) -app.listen(8000, () => console.log('Example app listening on port 8000!')) +app.listen(8000, () => console.log('Example app listening on http://127.0.0.1:8000!'))