| import { app } from "../../../scripts/app.js"; |
|
|
| |
|
|
| const LOCKED = Symbol(); |
|
|
| function lockArray(arr, isLocked) { |
| const v = []; |
|
|
| for (let i = 0; i < 2; i++) { |
| v[i] = arr[i]; |
|
|
| Object.defineProperty(arr, i, { |
| get() { |
| return v[i]; |
| }, |
| set(value) { |
| if (!isLocked()) { |
| v[i] = value; |
| } |
| }, |
| }); |
| } |
| } |
|
|
| app.registerExtension({ |
| name: "pysssss.Locking", |
| init() { |
| function lockGroup(node) { |
| node[LOCKED] = true; |
| } |
|
|
| |
| const serialize = LGraphGroup.prototype.serialize; |
| LGraphGroup.prototype.serialize = function () { |
| const o = serialize.apply(this, arguments); |
| o.locked = !!this[LOCKED]; |
| return o; |
| }; |
|
|
| |
| const configure = LGraphGroup.prototype.configure; |
| LGraphGroup.prototype.configure = function (o) { |
| configure.apply(this, arguments); |
| if (o.locked) { |
| lockGroup(this); |
| } |
| }; |
|
|
| |
| const getGroupOnPos = LGraph.prototype.getGroupOnPos; |
| LGraph.prototype.getGroupOnPos = function () { |
| const r = getGroupOnPos.apply(this, arguments); |
| if (r && r[LOCKED] && !new Error().stack.includes("processContextMenu")) return null; |
| return r; |
| }; |
|
|
| |
| const getGroupMenuOptions = LGraphCanvas.prototype.getGroupMenuOptions; |
| LGraphCanvas.prototype.getGroupMenuOptions = function (node) { |
| const opts = getGroupMenuOptions.apply(this, arguments); |
|
|
| opts.unshift( |
| node[LOCKED] |
| ? { |
| content: "Unlock", |
| callback: () => { |
| delete node[LOCKED]; |
| }, |
| } |
| : { |
| content: "Lock", |
| callback: () => lockGroup(node), |
| }, |
| null |
| ); |
|
|
| return opts; |
| }; |
| }, |
| setup() { |
| const drawNodeShape = LGraphCanvas.prototype.drawNodeShape; |
| LGraphCanvas.prototype.drawNodeShape = function (node, ctx, size, fgcolor, bgcolor, selected, mouse_over) { |
| const res = drawNodeShape.apply(this, arguments); |
|
|
| if (node[LOCKED]) { |
| ctx.fillText("🔒", node.size[0] - 20, -10); |
| } |
|
|
| return res; |
| }; |
| }, |
| async beforeRegisterNodeDef(nodeType) { |
| const nodesArray = (nodes) => { |
| if (nodes) { |
| if (nodes instanceof Array) { |
| return nodes; |
| } |
| return [nodes]; |
| } |
| return Object.values(app.canvas.selected_nodes); |
| }; |
| function unlockNode(nodes) { |
| nodes = nodesArray(nodes); |
| for (const node of nodes) { |
| delete node[LOCKED]; |
| } |
| app.graph.setDirtyCanvas(true, false); |
| } |
| function lockNode(nodes) { |
| nodes = nodesArray(nodes); |
| for (const node of nodes) { |
| if (node[LOCKED]) continue; |
|
|
| node[LOCKED] = true; |
| |
| lockArray(node.pos, () => !!node[LOCKED]); |
|
|
| |
| |
| const sz = [node.size[0], node.size[1]]; |
| Object.defineProperty(node, "size", { |
| get() { |
| return sz; |
| }, |
| set(value) { |
| if (!node[LOCKED]) { |
| sz[0] = value[0]; |
| sz[1] = value[1]; |
| } |
| }, |
| }); |
| |
| lockArray(sz, () => !!node[LOCKED]); |
| } |
|
|
| app.graph.setDirtyCanvas(true, false); |
| } |
|
|
| |
| const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions; |
| nodeType.prototype.getExtraMenuOptions = function (_, options) { |
| const r = getExtraMenuOptions ? getExtraMenuOptions.apply(this, arguments) : undefined; |
|
|
| options.splice( |
| options.findIndex((o) => o?.content === "Properties") + 1, |
| 0, |
| null, |
| this[LOCKED] |
| ? { |
| content: "Unlock", |
| callback: () => { |
| unlockNode(); |
| }, |
| } |
| : { |
| content: "Lock", |
| callback: () => lockNode(), |
| } |
| ); |
|
|
| return r; |
| }; |
|
|
| |
| const onSerialize = nodeType.prototype.onSerialize; |
| nodeType.prototype.onSerialize = function (o) { |
| if (onSerialize) { |
| onSerialize.apply(this, arguments); |
| } |
| o.locked = this[LOCKED]; |
| }; |
|
|
| |
| const onConfigure = nodeType.prototype.onConfigure; |
| nodeType.prototype.onConfigure = function (o) { |
| if (onConfigure) { |
| onConfigure.apply(this, arguments); |
| } |
| if (o.locked) { |
| lockNode(this); |
| } |
| }; |
| }, |
| }); |
|
|