* leak patch: prevent infinite duplication of MaskEditorDialog instance on every dialog open

* prevent conflict of multiple opening of MaskEditorDialog
* name of save button fix
This commit is contained in:
Lt.Dr.Data 2023-05-12 15:47:08 +09:00
parent 8a45286268
commit c30bc7c56c
2 changed files with 173 additions and 145 deletions

View File

@ -72,40 +72,50 @@ function prepareRGB(image, backupCanvas, backupCtx) {
class MaskEditorDialog extends ComfyDialog { class MaskEditorDialog extends ComfyDialog {
static instance = null; static instance = null;
static getInstance() {
if(!MaskEditorDialog.instance) {
MaskEditorDialog.instance = new MaskEditorDialog(app);
}
return MaskEditorDialog.instance;
}
is_layout_created = false;
constructor() { constructor() {
super(); super();
this.element = $el("div.comfy-modal", { parent: document.body }, this.element = $el("div.comfy-modal", { parent: document.body },
[ $el("div.comfy-modal-content", [ $el("div.comfy-modal-content",
[...this.createButtons()]), [...this.createButtons()]),
]); ]);
MaskEditorDialog.instance = this;
} }
createButtons() { createButtons() {
return []; return [];
} }
clearMask(self) {
}
createButton(name, callback) { createButton(name, callback) {
var button = document.createElement("button"); var button = document.createElement("button");
button.innerText = name; button.innerText = name;
button.addEventListener("click", callback); button.addEventListener("click", callback);
return button; return button;
} }
createLeftButton(name, callback) { createLeftButton(name, callback) {
var button = this.createButton(name, callback); var button = this.createButton(name, callback);
button.style.cssFloat = "left"; button.style.cssFloat = "left";
button.style.marginRight = "4px"; button.style.marginRight = "4px";
return button; return button;
} }
createRightButton(name, callback) { createRightButton(name, callback) {
var button = this.createButton(name, callback); var button = this.createButton(name, callback);
button.style.cssFloat = "right"; button.style.cssFloat = "right";
button.style.marginLeft = "4px"; button.style.marginLeft = "4px";
return button; return button;
} }
createLeftSlider(self, name, callback) { createLeftSlider(self, name, callback) {
const divElement = document.createElement('div'); const divElement = document.createElement('div');
divElement.id = "maskeditor-slider"; divElement.id = "maskeditor-slider";
@ -185,15 +195,11 @@ class MaskEditorDialog extends ComfyDialog {
var cancelButton = this.createRightButton("Cancel", () => { var cancelButton = this.createRightButton("Cancel", () => {
document.removeEventListener("mouseup", MaskEditorDialog.handleMouseUp); document.removeEventListener("mouseup", MaskEditorDialog.handleMouseUp);
document.removeEventListener("keydown", MaskEditorDialog.handleKeyDown); document.removeEventListener("keydown", MaskEditorDialog.handleKeyDown);
ComfyApp.onClipspaceEditorClosed(false);
self.close(); self.close();
}); });
var save_button_title = "Save"; this.saveButton = this.createRightButton("Save", () => {
if(ComfyApp.clipspace_return_node) {
save_button_title = "Save to node";
}
var saveButton = this.createRightButton(save_button_title, () => {
document.removeEventListener("mouseup", MaskEditorDialog.handleMouseUp); document.removeEventListener("mouseup", MaskEditorDialog.handleMouseUp);
document.removeEventListener("keydown", MaskEditorDialog.handleKeyDown); document.removeEventListener("keydown", MaskEditorDialog.handleKeyDown);
self.save(); self.save();
@ -205,11 +211,10 @@ class MaskEditorDialog extends ComfyDialog {
this.element.appendChild(bottom_panel); this.element.appendChild(bottom_panel);
bottom_panel.appendChild(clearButton); bottom_panel.appendChild(clearButton);
bottom_panel.appendChild(saveButton); bottom_panel.appendChild(this.saveButton);
bottom_panel.appendChild(cancelButton); bottom_panel.appendChild(cancelButton);
bottom_panel.appendChild(brush_size_slider); bottom_panel.appendChild(brush_size_slider);
this.element.style.display = "block";
imgCanvas.style.position = "relative"; imgCanvas.style.position = "relative";
imgCanvas.style.top = "200"; imgCanvas.style.top = "200";
imgCanvas.style.left = "0"; imgCanvas.style.left = "0";
@ -218,25 +223,45 @@ class MaskEditorDialog extends ComfyDialog {
} }
show() { show() {
// layout if(!this.is_layout_created) {
const imgCanvas = document.createElement('canvas'); // layout
const maskCanvas = document.createElement('canvas'); const imgCanvas = document.createElement('canvas');
const backupCanvas = document.createElement('canvas'); const maskCanvas = document.createElement('canvas');
const backupCanvas = document.createElement('canvas');
imgCanvas.id = "imageCanvas"; imgCanvas.id = "imageCanvas";
maskCanvas.id = "maskCanvas"; maskCanvas.id = "maskCanvas";
backupCanvas.id = "backupCanvas"; backupCanvas.id = "backupCanvas";
this.setlayout(imgCanvas, maskCanvas); this.setlayout(imgCanvas, maskCanvas);
// prepare content // prepare content
this.maskCanvas = maskCanvas; this.imgCanvas = imgCanvas;
this.backupCanvas = backupCanvas; this.maskCanvas = maskCanvas;
this.maskCtx = maskCanvas.getContext('2d'); this.backupCanvas = backupCanvas;
this.backupCtx = backupCanvas.getContext('2d'); this.maskCtx = maskCanvas.getContext('2d');
this.backupCtx = backupCanvas.getContext('2d');
this.setImages(imgCanvas, backupCanvas); this.setEventHandler(maskCanvas);
this.setEventHandler(maskCanvas);
this.is_layout_created = true;
}
this.setImages(this.imgCanvas, this.backupCanvas);
if(ComfyApp.clipspace_return_node) {
this.saveButton.innerText = "Save to node";
}
else {
this.saveButton.innerText = "Save";
}
this.element.style.display = "block";
this.element.style.zIndex = 8888; // NOTE: alert dialog must be high priority.
}
isOpened() {
return this.element.style.display == "block";
} }
setImages(imgCanvas, backupCanvas) { setImages(imgCanvas, backupCanvas) {
@ -245,6 +270,10 @@ class MaskEditorDialog extends ComfyDialog {
const maskCtx = this.maskCtx; const maskCtx = this.maskCtx;
const maskCanvas = this.maskCanvas; const maskCanvas = this.maskCanvas;
backupCtx.clearRect(0,0,this.backupCanvas.width,this.backupCanvas.height);
imgCtx.clearRect(0,0,this.imgCanvas.width,this.imgCanvas.height);
maskCtx.clearRect(0,0,this.maskCanvas.width,this.maskCanvas.height);
// image load // image load
const orig_image = new Image(); const orig_image = new Image();
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
@ -302,8 +331,7 @@ class MaskEditorDialog extends ComfyDialog {
rgb_url.searchParams.set('channel', 'rgb'); rgb_url.searchParams.set('channel', 'rgb');
orig_image.src = rgb_url; orig_image.src = rgb_url;
this.image = orig_image; this.image = orig_image;
}g }
setEventHandler(maskCanvas) { setEventHandler(maskCanvas) {
maskCanvas.addEventListener("contextmenu", (event) => { maskCanvas.addEventListener("contextmenu", (event) => {
@ -578,7 +606,7 @@ class MaskEditorDialog extends ComfyDialog {
await uploadMask(item, formData); await uploadMask(item, formData);
this.close(); this.close();
ComfyApp.onClipspaceEditorSaved(); ComfyApp.onClipspaceEditorClosed(true);
} }
} }
@ -587,8 +615,10 @@ app.registerExtension({
init(app) { init(app) {
ComfyApp.open_maskeditor = ComfyApp.open_maskeditor =
function () { function () {
let dlg = new MaskEditorDialog(app); const dlg = MaskEditorDialog.getInstance();
dlg.show(); if(!dlg.isOpened()) {
dlg.show();
}
}; };
const context_predicate = () => ComfyApp.clipspace && ComfyApp.clipspace.imgs && ComfyApp.clipspace.imgs.length > 0 const context_predicate = () => ComfyApp.clipspace && ComfyApp.clipspace.imgs && ComfyApp.clipspace.imgs.length > 0

View File

@ -51,106 +51,105 @@ export class ComfyApp {
this.shiftDown = false; this.shiftDown = false;
} }
static isImageNode(node) { static isImageNode(node) {
console.log(node); return node.imgs || (node && node.widgets && node.widgets.findIndex(obj => obj.name === 'image') >= 0);
return node.imgs || (node && node.widgets && node.widgets.findIndex(obj => obj.name === 'image') >= 0); }
}
static onClipspaceEditorSaved() { static onClipspaceEditorClosed(save_mode) {
if(ComfyApp.clipspace_return_node) { if(ComfyApp.clipspace_return_node) {
ComfyApp.pasteToClipspace(ComfyApp.clipspace_return_node); if(save_mode)
ComfyApp.clipspace_return_node = null; ComfyApp.pasteToClipspace(ComfyApp.clipspace_return_node);
} ComfyApp.clipspace_return_node = null;
} }
}
static copyToClipspace(node) { static copyToClipspace(node) {
var widgets = null; var widgets = null;
if(node.widgets) { if(node.widgets) {
widgets = node.widgets.map(({ type, name, value }) => ({ type, name, value })); widgets = node.widgets.map(({ type, name, value }) => ({ type, name, value }));
} }
var imgs = undefined; var imgs = undefined;
var orig_imgs = undefined; var orig_imgs = undefined;
if(node.imgs != undefined) { if(node.imgs != undefined) {
imgs = []; imgs = [];
orig_imgs = []; orig_imgs = [];
for (let i = 0; i < node.imgs.length; i++) { for (let i = 0; i < node.imgs.length; i++) {
imgs[i] = new Image(); imgs[i] = new Image();
imgs[i].src = node.imgs[i].src; imgs[i].src = node.imgs[i].src;
orig_imgs[i] = imgs[i]; orig_imgs[i] = imgs[i];
} }
} }
ComfyApp.clipspace = { ComfyApp.clipspace = {
'widgets': widgets, 'widgets': widgets,
'imgs': imgs, 'imgs': imgs,
'original_imgs': orig_imgs, 'original_imgs': orig_imgs,
'images': node.images, 'images': node.images,
'selectedIndex': 0, 'selectedIndex': 0,
'img_paste_mode': 'selected' // reset to default im_paste_mode state on copy action 'img_paste_mode': 'selected' // reset to default im_paste_mode state on copy action
}; };
ComfyApp.clipspace_return_node = null; ComfyApp.clipspace_return_node = null;
if(ComfyApp.clipspace_invalidate_handler) { if(ComfyApp.clipspace_invalidate_handler) {
ComfyApp.clipspace_invalidate_handler(); ComfyApp.clipspace_invalidate_handler();
} }
} }
static pasteToClipspace(node) { static pasteToClipspace(node) {
if(ComfyApp.clipspace) { if(ComfyApp.clipspace) {
// image paste // image paste
if(ComfyApp.clipspace.imgs && node.imgs) { if(ComfyApp.clipspace.imgs && node.imgs) {
if(node.images && ComfyApp.clipspace.images) { if(node.images && ComfyApp.clipspace.images) {
if(ComfyApp.clipspace['img_paste_mode'] == 'selected') { if(ComfyApp.clipspace['img_paste_mode'] == 'selected') {
app.nodeOutputs[node.id + ""].images = node.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]]; app.nodeOutputs[node.id + ""].images = node.images = [ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]];
}
else
app.nodeOutputs[node.id + ""].images = node.images = ComfyApp.clipspace.images;
}
} if(ComfyApp.clipspace.imgs) {
else // deep-copy to cut link with clipspace
app.nodeOutputs[node.id + ""].images = node.images = ComfyApp.clipspace.images; if(ComfyApp.clipspace['img_paste_mode'] == 'selected') {
} const img = new Image();
img.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src;
node.imgs = [img];
}
else {
const imgs = [];
for(let i=0; i<ComfyApp.clipspace.imgs.length; i++) {
imgs[i] = new Image();
imgs[i].src = ComfyApp.clipspace.imgs[i].src;
node.imgs = imgs;
}
}
}
}
if(ComfyApp.clipspace.imgs) { if(node.widgets) {
// deep-copy to cut link with clipspace if(ComfyApp.clipspace.images) {
if(ComfyApp.clipspace['img_paste_mode'] == 'selected') { const clip_image = ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']];
const img = new Image(); const index = node.widgets.findIndex(obj => obj.name === 'image');
img.src = ComfyApp.clipspace.imgs[ComfyApp.clipspace['selectedIndex']].src; if(index >= 0) {
node.imgs = [img]; node.widgets[index].value = clip_image;
} }
else { }
const imgs = []; if(ComfyApp.clipspace.widgets) {
for(let i=0; i<ComfyApp.clipspace.imgs.length; i++) { ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
imgs[i] = new Image(); const prop = Object.values(node.widgets).find(obj => obj.type === type && obj.name === name);
imgs[i].src = ComfyApp.clipspace.imgs[i].src; if (prop && prop.type != 'button') {
node.imgs = imgs; prop.value = value;
} prop.callback(value);
} }
} });
} }
}
if(node.widgets) { app.graph.setDirtyCanvas(true);
if(ComfyApp.clipspace.images) { }
const clip_image = ComfyApp.clipspace.images[ComfyApp.clipspace['selectedIndex']]; }
const index = node.widgets.findIndex(obj => obj.name === 'image');
if(index >= 0) {
node.widgets[index].value = clip_image;
}
}
if(ComfyApp.clipspace.widgets) {
ComfyApp.clipspace.widgets.forEach(({ type, name, value }) => {
const prop = Object.values(node.widgets).find(obj => obj.type === type && obj.name === name);
if (prop && prop.type != 'button') {
prop.value = value;
prop.callback(value);
}
});
}
}
app.graph.setDirtyCanvas(true);
}
}
/** /**
* Invoke an extension callback * Invoke an extension callback
@ -241,32 +240,31 @@ export class ComfyApp {
} }
} }
options.push( // prevent conflict of clipspace content
{ if(!ComfyApp.clipspace_return_node) {
content: "Copy (Clipspace)", options.push({
callback: (obj) => { ComfyApp.copyToClipspace(this); } content: "Copy (Clipspace)",
}); callback: (obj) => { ComfyApp.copyToClipspace(this); }
});
if(ComfyApp.clipspace != null) { if(ComfyApp.clipspace != null) {
options.push( options.push({
{ content: "Paste (Clipspace)",
content: "Paste (Clipspace)", callback: () => { ComfyApp.pasteToClipspace(this); }
callback: () => { ComfyApp.pasteToClipspace(this); } });
} }
);
if(ComfyApp.isImageNode(this)) {
options.push({
content: "Open in MaskEditor",
callback: (obj) => {
ComfyApp.copyToClipspace(this);
ComfyApp.clipspace_return_node = this;
ComfyApp.open_maskeditor();
}
});
}
} }
if(ComfyApp.isImageNode(this)) {
options.push(
{
content: "Open in MaskEditor",
callback: (obj) => {
ComfyApp.copyToClipspace(this);
ComfyApp.clipspace_return_node = this;
ComfyApp.open_maskeditor();
}
});
}
}; };
} }