|
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); |
|
} |
|
}; |
|
}, |
|
}); |
|
|