Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 148 additions & 2 deletions web/js/snapToGrid.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { app } from "../../../scripts/app.js";
import { $el } from "../../../scripts/ui.js";


let setting;
let setting, guide_setting, guide_config;
const id = "pysssss.SnapToGrid";
const guide_id = id + ".Guide";
const guide_config_default = {
lines: {
enabled: false,
fillStyle: "rgba(255, 0, 0, 0.5)",
},
block: {
enabled: false,
fillStyle: "rgba(0, 0, 255, 0.5)",
},

/** Wraps the provided function call to set/reset shiftDown when setting is enabled. */
function wrapCallInSettingCheck(fn) {
Expand All @@ -19,6 +29,11 @@ function wrapCallInSettingCheck(fn) {
const ext = {
name: id,
init() {
if (localStorage.getItem(guide_id) === null) {
localStorage.setItem(guide_id, JSON.stringify(guide_config_default));
}
guide_config = JSON.parse(localStorage.getItem(guide_id));

setting = app.ui.settings.addSetting({
id,
name: "🐍 Always snap to grid",
Expand All @@ -29,6 +44,81 @@ const ext = {
},
});

guide_setting = app.ui.settings.addSetting({
id: id + ".Guide",
name: "🐍 Display drag-and-drop guides",
type: (name, setter, value) => {
return $el("tr", [
$el("td", [
$el("label", {
for: id.replaceAll(".", "-"),
textContent: name,
}),
]),
$el("td", [
$el(
"label",
{
textContent: "Lines: ",
style: {
display: "inline-block",
},
},
[
$el("input", {
id: id.replaceAll(".", "-") + "-line-text",
type: "text",
value: guide_config.lines.fillStyle,
onchange: (event) => {
guide_config.lines.fillStyle = event.target.value;
localStorage.setItem(guide_id, JSON.stringify(guide_config));
}
}),
$el("input", {
id: id.replaceAll(".", "-") + "-line-checkbox",
type: "checkbox",
checked: guide_config.lines.enabled,
onchange: (event) => {
guide_config.lines.enabled = !!event.target.checked;
localStorage.setItem(guide_id, JSON.stringify(guide_config));
},
}),
]
),
$el(
"label",
{
textContent: "Block: ",
style: {
display: "inline-block",
},
},
[
$el("input", {
id: id.replaceAll(".", "-") + "-block-text",
type: "text",
value: guide_config.block.fillStyle,
onchange: (event) => {
guide_config.block.fillStyle = event.target.value;
localStorage.setItem(guide_id, JSON.stringify(guide_config));
}
}),
$el("input", {
id: id.replaceAll(".", "-") + '-block-checkbox',
type: "checkbox",
checked: guide_config.block.enabled,
onchange: (event) => {
guide_config.block.enabled = !!event.target.checked;
localStorage.setItem(guide_id, JSON.stringify(guide_config));
},
}),
]
),
]),
]);
}
});

// We need to register our hooks after the core snap to grid extension runs
// Do this from the graph configure function so we still get onNodeAdded calls
const configure = LGraph.prototype.configure;
Expand Down Expand Up @@ -68,6 +158,62 @@ const ext = {

return configure.apply(this, arguments);
};

// Override drag-and-drop behavior to show orthogonal guide lines around selected node(s) and preview of where the node(s) will be placed
const origDrawNode = LGraphCanvas.prototype.drawNode
LGraphCanvas.prototype.drawNode = function (node, ctx) {
const enabled = guide_config.lines.enabled || guide_config.block.enabled;
if (enabled && app.shiftDown && this.node_dragged && node.id in this.selected_nodes) {
// discretize the canvas into grid
let x = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[0] / LiteGraph.CANVAS_GRID_SIZE);
let y = LiteGraph.CANVAS_GRID_SIZE * Math.round(node.pos[1] / LiteGraph.CANVAS_GRID_SIZE);

// calculate the width and height of the node
// (also need to shift the y position of the node, depending on whether the title is visible)
x -= node.pos[0];
y -= node.pos[1];
let w, h;
if (node.flags.collapsed) {
w = node._collapsed_width;
h = LiteGraph.NODE_TITLE_HEIGHT;
y -= 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;
y -= LiteGraph.NODE_TITLE_HEIGHT;
}
}

// save the original fill style
const f = ctx.fillStyle;

// draw preview for drag-and-drop (rectangle to show where the node will be placed)
if (guide_config.block.enabled) {
ctx.fillStyle = guide_config.block.fillStyle;
ctx.fillRect(x, y, w, h);
}

// add guide lines around node (arbitrarily long enough to span most workflows)
if (guide_config.lines.enabled) {
const xd = 10000;
const yd = 10000;
const thickness = 3;
ctx.fillStyle = guide_config.lines.fillStyle;
ctx.fillRect(x - xd, y, 2*xd, thickness);
ctx.fillRect(x, y - yd, thickness, 2*yd);
ctx.fillRect(x - xd, y + h, 2*xd, thickness);
ctx.fillRect(x + w, y - yd, thickness, 2*yd);
}

// restore the original fill style
ctx.fillStyle = f;
}

return origDrawNode.apply(this, arguments);
};
},
};

Expand Down