From 5aefd6cdf3504119e10132f44dd5863581dc337d Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sun, 2 Apr 2023 15:16:08 +0100 Subject: [PATCH 1/4] Support numeric settings, tooltip, extra attrs --- web/scripts/ui.js | 86 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/web/scripts/ui.js b/web/scripts/ui.js index c27fbf9..679f10b 100644 --- a/web/scripts/ui.js +++ b/web/scripts/ui.js @@ -198,7 +198,7 @@ class ComfySettingsDialog extends ComfyDialog { localStorage[settingId] = JSON.stringify(value); } - addSetting({ id, name, type, defaultValue, onChange }) { + addSetting({ id, name, type, defaultValue, onChange, attrs = {}, tooltip = "", }) { if (!id) { throw new Error("Settings must have an ID"); } @@ -225,42 +225,72 @@ class ComfySettingsDialog extends ComfyDialog { value = v; }; + let element; + if (typeof type === "function") { - return type(name, setter, value); + element = type(name, setter, value, attrs); + } else { + switch (type) { + case "boolean": + element = $el("div", [ + $el("label", { textContent: name || id }, [ + $el("input", { + type: "checkbox", + checked: !!value, + oninput: (e) => { + setter(e.target.checked); + }, + ...attrs + }), + ]), + ]); + break; + case "number": + element = $el("div", [ + $el("label", { textContent: name || id }, [ + $el("input", { + type, + value, + oninput: (e) => { + setter(e.target.value); + }, + ...attrs + }), + ]), + ]); + break; + default: + console.warn("Unsupported setting type, defaulting to text"); + element = $el("div", [ + $el("label", { textContent: name || id }, [ + $el("input", { + value, + oninput: (e) => { + setter(e.target.value); + }, + ...attrs + }), + ]), + ]); + break; + } } - - switch (type) { - case "boolean": - return $el("div", [ - $el("label", { textContent: name || id }, [ - $el("input", { - type: "checkbox", - checked: !!value, - oninput: (e) => { - setter(e.target.checked); - }, - }), - ]), - ]); - default: - console.warn("Unsupported setting type, defaulting to text"); - return $el("div", [ - $el("label", { textContent: name || id }, [ - $el("input", { - value, - oninput: (e) => { - setter(e.target.value); - }, - }), - ]), - ]); + if(tooltip) { + element.title = tooltip; } + + return element; }, }); } show() { super.show(); + Object.assign(this.textElement.style, { + display: "flex", + flexDirection: "column", + gap: "10px" + }); this.textElement.replaceChildren(...this.settings.map((s) => s.render())); } } From d027ff121c904f5d21b5d9fd8607fcdb2b166ec3 Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sun, 2 Apr 2023 15:33:34 +0100 Subject: [PATCH 2/4] Snap to grid --- web/extensions/core/snapToGrid.js | 86 +++++++++++++++++++++++++++++++ web/scripts/app.js | 8 ++- 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 web/extensions/core/snapToGrid.js diff --git a/web/extensions/core/snapToGrid.js b/web/extensions/core/snapToGrid.js new file mode 100644 index 0000000..80e836a --- /dev/null +++ b/web/extensions/core/snapToGrid.js @@ -0,0 +1,86 @@ +import { app } from "/scripts/app.js"; + +// Shift + drag/resize to snap to grid + +app.registerExtension({ + name: "Comfy.SnapToGrid", + init() { + // Add setting to control grid size + app.ui.settings.addSetting({ + id: "Comfy.SnapToGrid.GridSize", + name: "Grid Size", + type: "number", + attrs: { + min: 1, + max: 500, + }, + tooltip: + "When dragging and resizing nodes while holding shift they will be aligned to the grid, this controls the size of that grid.", + defaultValue: LiteGraph.CANVAS_GRID_SIZE, + onChange(value) { + LiteGraph.CANVAS_GRID_SIZE = +value; + }, + }); + + // After moving a node, if the shift key is down align it to grid + const onNodeMoved = app.canvas.onNodeMoved; + app.canvas.onNodeMoved = function (node) { + const r = onNodeMoved?.apply(this, arguments); + + if (app.shiftDown) { + node.alignToGrid(); + } + + return r; + }; + + // When a node is added, add a resize handler to it so we can fix align the size with the grid + const onNodeAdded = app.graph.onNodeAdded; + app.graph.onNodeAdded = function (node) { + const onResize = node.onResize; + node.onResize = function () { + if(app.shiftDown) { + const w = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[0] / LiteGraph.CANVAS_GRID_SIZE); + const h = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[1] / LiteGraph.CANVAS_GRID_SIZE); + node.size[0] = w; + node.size[1] = h; + } + return onResize?.apply(this, arguments); + }; + return onNodeAdded?.apply(this, arguments); + }; + + // Draw a preview of where the node will go if holding shift + const origDrawNode = LGraphCanvas.prototype.drawNode; + LGraphCanvas.prototype.drawNode = function (node, ctx) { + if (app.shiftDown && node === this.node_dragged) { + const x = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[0] / LiteGraph.CANVAS_GRID_SIZE); + const y = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[1] / LiteGraph.CANVAS_GRID_SIZE); + + const shiftX = x - node.pos[0]; + let shiftY = y - node.pos[1]; + + let w, h; + if (node.flags.collapsed) { + w = node._collapsed_width; + h = LiteGraph.NODE_TITLE_HEIGHT; + shiftY -= LiteGraph.NODE_TITLE_HEIGHT; + } else { + w = node.size[0]; + h = node.size[1]; + let titleMode = node.constructor.title_mode; + if (titleMode !== LiteGraph.TRANSPARENT_TITLE && titleMode !== LiteGraph.NO_TITLE) { + h += LiteGraph.NODE_TITLE_HEIGHT; + shiftY -= LiteGraph.NODE_TITLE_HEIGHT; + } + } + const f = ctx.fillStyle; + ctx.fillStyle = "rgba(100, 100, 100, 0.5)"; + ctx.fillRect(shiftX, shiftY, w, h); + ctx.fillStyle = f; + } + + return origDrawNode.apply(this, arguments); + }; + }, +}); diff --git a/web/scripts/app.js b/web/scripts/app.js index 5af6d5f..6f8ac06 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -18,6 +18,7 @@ class ComfyApp { this.ui = new ComfyUI(this); this.extensions = []; this.nodeOutputs = {}; + this.shiftDown = false; } /** @@ -538,7 +539,7 @@ class ComfyApp { color = "#0f0"; } else if (self.dragOverNode && node.id === self.dragOverNode.id) { color = "dodgerblue"; - } + } if (color) { const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; @@ -637,11 +638,16 @@ class ComfyApp { #addKeyboardHandler() { window.addEventListener("keydown", (e) => { + this.shiftDown = e.shiftKey; + // Queue prompt using ctrl or command + enter if ((e.ctrlKey || e.metaKey) && (e.key === "Enter" || e.keyCode === 13 || e.keyCode === 10)) { this.queuePrompt(e.shiftKey ? -1 : 0); } }); + window.addEventListener("keyup", (e) => { + this.shiftDown = e.shiftKey; + }); } /** From 26dc8e3056c18bf08e83cfc868665cea25a90868 Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sun, 2 Apr 2023 15:36:27 +0100 Subject: [PATCH 3/4] formatting --- web/scripts/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/scripts/app.js b/web/scripts/app.js index 6f8ac06..8612d5a 100644 --- a/web/scripts/app.js +++ b/web/scripts/app.js @@ -539,7 +539,7 @@ class ComfyApp { color = "#0f0"; } else if (self.dragOverNode && node.id === self.dragOverNode.id) { color = "dodgerblue"; - } + } if (color) { const shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; From 04234152c14e58cf1e09abad442b0586c5bf2339 Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Sun, 2 Apr 2023 19:12:00 +0100 Subject: [PATCH 4/4] Add support for multiselect --- web/extensions/core/snapToGrid.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/web/extensions/core/snapToGrid.js b/web/extensions/core/snapToGrid.js index 80e836a..20b245e 100644 --- a/web/extensions/core/snapToGrid.js +++ b/web/extensions/core/snapToGrid.js @@ -1,6 +1,6 @@ import { app } from "/scripts/app.js"; -// Shift + drag/resize to snap to grid +// Shift + drag/resize to snap to grid app.registerExtension({ name: "Comfy.SnapToGrid", @@ -28,32 +28,35 @@ app.registerExtension({ const r = onNodeMoved?.apply(this, arguments); if (app.shiftDown) { - node.alignToGrid(); + // Ensure all selected nodes are realigned + for (const id in this.selected_nodes) { + this.selected_nodes[id].alignToGrid(); + } } return r; }; - // When a node is added, add a resize handler to it so we can fix align the size with the grid + // When a node is added, add a resize handler to it so we can fix align the size with the grid const onNodeAdded = app.graph.onNodeAdded; app.graph.onNodeAdded = function (node) { const onResize = node.onResize; node.onResize = function () { - if(app.shiftDown) { - const w = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[0] / LiteGraph.CANVAS_GRID_SIZE); - const h = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[1] / LiteGraph.CANVAS_GRID_SIZE); - node.size[0] = w; - node.size[1] = h; - } + if (app.shiftDown) { + const w = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[0] / LiteGraph.CANVAS_GRID_SIZE); + const h = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.size[1] / LiteGraph.CANVAS_GRID_SIZE); + node.size[0] = w; + node.size[1] = h; + } return onResize?.apply(this, arguments); }; return onNodeAdded?.apply(this, arguments); }; - // Draw a preview of where the node will go if holding shift + // Draw a preview of where the node will go if holding shift and the node is selected const origDrawNode = LGraphCanvas.prototype.drawNode; LGraphCanvas.prototype.drawNode = function (node, ctx) { - if (app.shiftDown && node === this.node_dragged) { + if (app.shiftDown && this.node_dragged && node.id in this.selected_nodes) { const x = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[0] / LiteGraph.CANVAS_GRID_SIZE); const y = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[1] / LiteGraph.CANVAS_GRID_SIZE);