diff --git a/src/extensions/core/snapToGrid.ts b/src/extensions/core/snapToGrid.ts index 3f10c37f3..2865c0d11 100644 --- a/src/extensions/core/snapToGrid.ts +++ b/src/extensions/core/snapToGrid.ts @@ -1,23 +1,5 @@ -// @ts-strict-ignore -import type { SettingParams } from '@/types/settingTypes' import { app } from '../../scripts/app' -import { - LGraphCanvas, - LGraphNode, - LGraphGroup, - LiteGraph -} from '@comfyorg/litegraph' - -// Shift + drag/resize to snap to grid - -/** Rounds a Vector2 in-place to the current CANVAS_GRID_SIZE. */ -function roundVectorToGrid(vec) { - vec[0] = - LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[0] / LiteGraph.CANVAS_GRID_SIZE) - vec[1] = - LiteGraph.CANVAS_GRID_SIZE * Math.round(vec[1] / LiteGraph.CANVAS_GRID_SIZE) - return vec -} +import { LiteGraph } from '@comfyorg/litegraph' app.registerExtension({ name: 'Comfy.SnapToGrid', @@ -39,164 +21,20 @@ app.registerExtension({ LiteGraph.CANVAS_GRID_SIZE = +value || 10 } }) + // Keep the 'pysssss.SnapToGrid' setting id so we don't need to migrate setting values. // Using a new setting id can cause existing users to lose their existing settings. - const alwaysSnapToGrid = app.ui.settings.addSetting({ + app.ui.settings.addSetting({ id: 'pysssss.SnapToGrid', category: ['LiteGraph', 'Canvas', 'AlwaysSnapToGrid'], name: 'Always snap to grid', type: 'boolean', defaultValue: false, - versionAdded: '1.3.13' - } as SettingParams) - - const shouldSnapToGrid = () => app.shiftDown || alwaysSnapToGrid.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 (shouldSnapToGrid()) { - // Ensure all selected nodes are realigned - for (const id in this.selected_nodes) { - this.selected_nodes[id].alignToGrid() - } + versionAdded: '1.3.13', + versionModified: '1.3.42', + onChange(value) { + app.graph.config.alwaysSnapToGrid = !!value } - - 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 (shouldSnapToGrid()) { - roundVectorToGrid(node.size) - } - return onResize?.apply(this, arguments) - } - return onNodeAdded?.apply(this, arguments) - } - - // 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 ( - shouldSnapToGrid() && - this.node_dragged && - node.id in this.selected_nodes - ) { - const [x, y] = roundVectorToGrid([...node.pos]) - 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] - const 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) - } - - /** - * The currently moving, selected group only. Set after the `selected_group` has actually started - * moving. - */ - let selectedAndMovingGroup: LGraphGroup | null = null - - /** - * Handles moving a group; tracking when a group has been moved (to show the ghost in `drawGroups` - * below) as well as handle the last move call from LiteGraph's `processMouseUp`. - */ - const groupMove = LGraphGroup.prototype.move - LGraphGroup.prototype.move = function (deltax, deltay, ignore_nodes) { - const v = groupMove.apply(this, arguments) - // When we've started moving, set `selectedAndMovingGroup` as LiteGraph sets `selected_group` - // too eagerly and we don't want to behave like we're moving until we get a delta. - if ( - !selectedAndMovingGroup && - app.canvas.selected_group === this && - (deltax || deltay) - ) { - selectedAndMovingGroup = this - } - - // LiteGraph will call group.move both on mouse-move as well as mouse-up though we only want - // to snap on a mouse-up which we can determine by checking if `app.canvas.last_mouse_dragging` - // has been set to `false`. Essentially, this check here is the equivalent to calling an - // `LGraphGroup.prototype.onNodeMoved` if it had existed. - if (app.canvas.last_mouse_dragging === false && shouldSnapToGrid()) { - // After moving a group (while shouldSnapToGrid()), snap all the child nodes and, finally, - // align the group itself. - this.recomputeInsideNodes() - for (const node of this.nodes) { - node.alignToGrid() - } - LGraphNode.prototype.alignToGrid.apply(this) - } - return v - } - - /** - * Handles drawing a group when, snapping the size when one is actively being resized tracking and/or - * drawing a ghost box when one is actively being moved. This mimics the node snapping behavior for - * both. - */ - const drawGroups = LGraphCanvas.prototype.drawGroups - LGraphCanvas.prototype.drawGroups = function (canvas, ctx) { - if (this.selected_group && shouldSnapToGrid()) { - if (this.selected_group_resizing) { - roundVectorToGrid(this.selected_group.size) - } else if (selectedAndMovingGroup) { - const [x, y] = roundVectorToGrid([...selectedAndMovingGroup.pos]) - const f = ctx.fillStyle - const s = ctx.strokeStyle - ctx.fillStyle = 'rgba(100, 100, 100, 0.33)' - ctx.strokeStyle = 'rgba(100, 100, 100, 0.66)' - ctx.rect(x, y, ...(selectedAndMovingGroup.size as [number, number])) - ctx.fill() - ctx.stroke() - ctx.fillStyle = f - ctx.strokeStyle = s - } - } else if (!this.selected_group) { - selectedAndMovingGroup = null - } - return drawGroups.apply(this, arguments) - } - - /** Handles adding a group in a snapping-enabled state. */ - const onGroupAdd = LGraphCanvas.onGroupAdd - LGraphCanvas.onGroupAdd = function () { - const v = onGroupAdd.apply(app.canvas, arguments) - if (shouldSnapToGrid()) { - const lastGroup = app.graph.groups[app.graph.groups.length - 1] - if (lastGroup) { - roundVectorToGrid(lastGroup.pos) - roundVectorToGrid(lastGroup.size) - } - } - return v - } + }) } })