|
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.getBounding()[2] - 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);
|
|
}
|
|
};
|
|
},
|
|
});
|
|
|