From 518d8eccf8cebc6f733d3ebc3b29062d66ef489a Mon Sep 17 00:00:00 2001 From: rgthree Date: Tue, 5 Dec 2023 22:28:12 -0500 Subject: [PATCH] Extend rgthree and dialog from eventtarget; add showHelp method to base node; --- ts/base_node.ts | 25 ++++++++++++++++ ts/dialog.ts | 15 ++++++++-- ts/rgthree.ts | 75 ++++++++++++++++++++++++------------------------ ts/utils.ts | 7 ++++- web/base_node.js | 21 ++++++++++++++ web/dialog.js | 13 +++++++-- web/rgthree.css | 1 + web/rgthree.js | 66 +++++++++++++++++++++--------------------- web/utils.js | 7 ++++- 9 files changed, 152 insertions(+), 78 deletions(-) diff --git a/ts/base_node.ts b/ts/base_node.ts index 9afdf13..1e08875 100644 --- a/ts/base_node.ts +++ b/ts/base_node.ts @@ -8,6 +8,7 @@ import { app } from "../../scripts/app.js"; import { rgthree } from "./rgthree.js"; import { addHelpMenuItem } from "./utils.js"; +import { RgthreeHelpDialog } from "./dialog.js"; declare const LGraphNode: typeof TLGraphNode; declare const LiteGraph: typeof TLiteGraph; @@ -39,6 +40,8 @@ export class RgthreeBaseNode extends LGraphNode { removed = false; configuring = false; + helpDialog: RgthreeHelpDialog | null = null; + constructor(title = RgthreeBaseNode.title) { super(title); if (title == '__NEED_NAME__') { @@ -113,6 +116,28 @@ export class RgthreeBaseNode extends LGraphNode { return ''; } + showHelp() { + const help = this.getHelp() || (this.constructor as any).help; + if (help) { + this.helpDialog = new RgthreeHelpDialog(this, help).show(); + this.helpDialog.addEventListener('close', (e) => { + console.log('close', e); + this.helpDialog = null; + }); + } + } + + override onKeyDown(event: KeyboardEvent): void { + rgthree.handleKeydown(event); + if (event.key == '?' && !this.helpDialog) { + this.showHelp(); + } + } + + override onKeyUp(event: KeyboardEvent): void { + rgthree.handleKeyup(event); + } + override getExtraMenuOptions(canvas: LGraphCanvas, options: ContextMenuItem[]): void { // Some other extensions override getExtraMenuOptions on the nodeType as it comes through from // the server, so we can call out to that if we don't have our own. diff --git a/ts/dialog.ts b/ts/dialog.ts index e6909f1..b684699 100644 --- a/ts/dialog.ts +++ b/ts/dialog.ts @@ -14,10 +14,11 @@ type RgthreeDialogOptions = { /** * A Dialog that shows content, and closes. */ -export class RgthreeDialog { +export class RgthreeDialog extends EventTarget { element: HTMLDialogElement; constructor(options: RgthreeDialogOptions) { + super(); let contentEl = $el("div.rgthree-dialog-container"); this.element = $el("dialog", { classes: ["rgthree-dialog", options.class || ""], @@ -32,7 +33,7 @@ export class RgthreeDialog { event.clientX < rect.left || event.clientX > rect.right ) { - return this.element.close(); + return this.close(); } }, }, @@ -56,7 +57,7 @@ export class RgthreeDialog { text: options.closeButtonLabel || "Close", events: { click: () => { - this.element.close(); + this.close(); }, }, }), @@ -65,7 +66,15 @@ export class RgthreeDialog { show() { this.element.showModal(); + this.dispatchEvent(new CustomEvent('show')); + return this; } + + close() { + this.element.close(); + this.dispatchEvent(new CustomEvent('close')); + } + } /** diff --git a/ts/rgthree.ts b/ts/rgthree.ts index 5e97056..b289254 100644 --- a/ts/rgthree.ts +++ b/ts/rgthree.ts @@ -90,12 +90,13 @@ export type RgthreeUiMessage = { /** * A global class as 'rgthree'; exposed on wiindow. Lots can go in here. */ -class Rgthree { +class Rgthree extends EventTarget { /** Are any functional keys pressed in this given moment? */ ctrlKey = false; altKey = false; metaKey = false; shiftKey = false; + readonly downKeys: {[key:string]: boolean} = {}; logger = new LogSession("[rgthree]"); @@ -175,19 +176,43 @@ class Rgthree { msg && msg.remove(); } + /** Handle keydown. Pulled out because sometimes a node will get a keydown before rgthree. */ + handleKeydown(e: KeyboardEvent) { + this.ctrlKey = !!e.ctrlKey; + this.altKey = !!e.altKey; + this.metaKey = !!e.metaKey; + this.shiftKey = !!e.shiftKey; + this.downKeys[e.key.toLocaleUpperCase()] = true; + this.downKeys['^' + e.key.toLocaleUpperCase()] = true; + } + + /** Handle keyup. Pulled out because sometimes a node will get a keyup before rgthree. */ + handleKeyup(e: KeyboardEvent) { + this.ctrlKey = !!e.ctrlKey; + this.altKey = !!e.altKey; + this.metaKey = !!e.metaKey; + this.shiftKey = !!e.shiftKey; + this.downKeys[e.key.toLocaleUpperCase()] = false; + this.downKeys['^' + e.key.toLocaleUpperCase()] = false; + } + + areAllKeysDown(keys: string[], caseSensitive = false) { + return keys.every((k) => { + if (caseSensitive) { + return rgthree.downKeys['^' + k.trim()]; + } + return rgthree.downKeys[k.trim().toUpperCase()]; + }); + } + constructor() { + super() window.addEventListener("keydown", (e) => { - this.ctrlKey = !!e.ctrlKey; - this.altKey = !!e.altKey; - this.metaKey = !!e.metaKey; - this.shiftKey = !!e.shiftKey; + this.handleKeydown(e); }); window.addEventListener("keyup", (e) => { - this.ctrlKey = !!e.ctrlKey; - this.altKey = !!e.altKey; - this.metaKey = !!e.metaKey; - this.shiftKey = !!e.shiftKey; + this.handleKeyup(e); }); // Override the loadGraphData so we can check for bad links and ask the user to fix them. @@ -195,22 +220,22 @@ class Rgthree { const queuePrompt = app.queuePrompt as Function; app.queuePrompt = async function() { - that.fireEvent('queue', {}); + that.dispatchEvent(new CustomEvent('queue')); that.processingQueue = true; try { await queuePrompt.apply(app, [...arguments]) } finally { that.processingQueue = false; - that.fireEvent('queue-end', {}); + that.dispatchEvent(new CustomEvent('queue-end')); } } const graphToPrompt = app.graphToPrompt as Function; app.graphToPrompt = async function() { - that.fireEvent('graph-to-prompt', {}); + that.dispatchEvent(new CustomEvent('graph-to-prompt')); let promise = graphToPrompt.apply(app, [...arguments]); await promise; - that.fireEvent('graph-to-prompt-end', {}); + that.dispatchEvent(new CustomEvent('graph-to-prompt-end')); return promise; } @@ -302,30 +327,6 @@ class Rgthree { document.head.appendChild(link); } - private readonly eventsToFns = new Map void>>(); - - addEventListener(event: string, fn: (ev: Event) => void) { - if (!this.eventsToFns.has(event)) { - this.eventsToFns.set(event, new Set()); - } - this.eventsToFns.get(event)!.add(fn); - } - - removeEventListener(event: string, fn: (ev: Event) => void) { - if (this.eventsToFns.has(event)) { - this.eventsToFns.get(event)!.delete(fn); - } - } - - fireEvent(event: string, data: any) { - if (this.eventsToFns.has(event)) { - for (let fn of this.eventsToFns.get(event)!) { - const event = new Event(data); - fn(event); - } - } - } - setLogLevel(level: LogLevel) { GLOBAL_LOG_LEVEL = level; } diff --git a/ts/utils.ts b/ts/utils.ts index 7982a3a..e5de477 100644 --- a/ts/utils.ts +++ b/ts/utils.ts @@ -356,7 +356,12 @@ export function addHelpMenuItem(node: TLGraphNode, content: string, menuOptions: addMenuItemOnExtraMenuOptions(node, { name: "🛟 Node Help", callback: (node) => { - new RgthreeHelpDialog(node, content).show(); + if ((node as any).showHelp) { + (node as any).showHelp(); + } else { + new RgthreeHelpDialog(node, content).show(); + } + }, }, menuOptions, 'Properties Panel'); } diff --git a/web/base_node.js b/web/base_node.js index 518da9b..6816869 100644 --- a/web/base_node.js +++ b/web/base_node.js @@ -2,6 +2,7 @@ import { ComfyWidgets } from "../../scripts/widgets.js"; import { app } from "../../scripts/app.js"; import { rgthree } from "./rgthree.js"; import { addHelpMenuItem } from "./utils.js"; +import { RgthreeHelpDialog } from "./dialog.js"; export class RgthreeBaseNode extends LGraphNode { constructor(title = RgthreeBaseNode.title) { super(title); @@ -9,6 +10,7 @@ export class RgthreeBaseNode extends LGraphNode { this.isVirtualNode = false; this.removed = false; this.configuring = false; + this.helpDialog = null; if (title == '__NEED_NAME__') { throw new Error('RgthreeBaseNode needs overrides.'); } @@ -57,6 +59,25 @@ export class RgthreeBaseNode extends LGraphNode { getHelp() { return ''; } + showHelp() { + const help = this.getHelp() || this.constructor.help; + if (help) { + this.helpDialog = new RgthreeHelpDialog(this, help).show(); + this.helpDialog.addEventListener('close', (e) => { + console.log('close', e); + this.helpDialog = null; + }); + } + } + onKeyDown(event) { + rgthree.handleKeydown(event); + if (event.key == '?' && !this.helpDialog) { + this.showHelp(); + } + } + onKeyUp(event) { + rgthree.handleKeyup(event); + } getExtraMenuOptions(canvas, options) { var _a, _b, _c, _d, _e, _f; if (super.getExtraMenuOptions) { diff --git a/web/dialog.js b/web/dialog.js index 084bdeb..8c6b01b 100644 --- a/web/dialog.js +++ b/web/dialog.js @@ -1,6 +1,7 @@ import { createElement as $el } from "./utils_dom.js"; -export class RgthreeDialog { +export class RgthreeDialog extends EventTarget { constructor(options) { + super(); let contentEl = $el("div.rgthree-dialog-container"); this.element = $el("dialog", { classes: ["rgthree-dialog", options.class || ""], @@ -13,7 +14,7 @@ export class RgthreeDialog { event.clientY > rect.bottom || event.clientX < rect.left || event.clientX > rect.right) { - return this.element.close(); + return this.close(); } }, }, @@ -34,7 +35,7 @@ export class RgthreeDialog { text: options.closeButtonLabel || "Close", events: { click: () => { - this.element.close(); + this.close(); }, }, }), @@ -42,6 +43,12 @@ export class RgthreeDialog { } show() { this.element.showModal(); + this.dispatchEvent(new CustomEvent('show')); + return this; + } + close() { + this.element.close(); + this.dispatchEvent(new CustomEvent('close')); } } export class RgthreeHelpDialog extends RgthreeDialog { diff --git a/web/rgthree.css b/web/rgthree.css index ec0f2fe..16c7839 100644 --- a/web/rgthree.css +++ b/web/rgthree.css @@ -119,6 +119,7 @@ height: 186px; line-height: 1; pointer-events: none; + z-index: -1; } @media (max-width: 832px) { diff --git a/web/rgthree.js b/web/rgthree.js index ece78a4..9a13ede 100644 --- a/web/rgthree.js +++ b/web/rgthree.js @@ -58,7 +58,7 @@ class LogSession { return new LogSession(`${this.name}${name}`); } } -class Rgthree { +class Rgthree extends EventTarget { async clearAllMessages() { let container = document.querySelector('.rgthree-top-messages-container'); container && (container.innerHTML = ''); @@ -117,47 +117,66 @@ class Rgthree { } msg && msg.remove(); } + handleKeydown(e) { + this.ctrlKey = !!e.ctrlKey; + this.altKey = !!e.altKey; + this.metaKey = !!e.metaKey; + this.shiftKey = !!e.shiftKey; + this.downKeys[e.key.toLocaleUpperCase()] = true; + this.downKeys['^' + e.key.toLocaleUpperCase()] = true; + } + handleKeyup(e) { + this.ctrlKey = !!e.ctrlKey; + this.altKey = !!e.altKey; + this.metaKey = !!e.metaKey; + this.shiftKey = !!e.shiftKey; + this.downKeys[e.key.toLocaleUpperCase()] = false; + this.downKeys['^' + e.key.toLocaleUpperCase()] = false; + } + areAllKeysDown(keys, caseSensitive = false) { + return keys.every((k) => { + if (caseSensitive) { + return rgthree.downKeys['^' + k.trim()]; + } + return rgthree.downKeys[k.trim().toUpperCase()]; + }); + } constructor() { + super(); this.ctrlKey = false; this.altKey = false; this.metaKey = false; this.shiftKey = false; + this.downKeys = {}; this.logger = new LogSession("[rgthree]"); this.monitorBadLinksAlerted = false; this.monitorLinkTimeout = null; this.processingQueue = false; - this.eventsToFns = new Map(); window.addEventListener("keydown", (e) => { - this.ctrlKey = !!e.ctrlKey; - this.altKey = !!e.altKey; - this.metaKey = !!e.metaKey; - this.shiftKey = !!e.shiftKey; + this.handleKeydown(e); }); window.addEventListener("keyup", (e) => { - this.ctrlKey = !!e.ctrlKey; - this.altKey = !!e.altKey; - this.metaKey = !!e.metaKey; - this.shiftKey = !!e.shiftKey; + this.handleKeyup(e); }); const that = this; const queuePrompt = app.queuePrompt; app.queuePrompt = async function () { - that.fireEvent('queue', {}); + that.dispatchEvent(new CustomEvent('queue')); that.processingQueue = true; try { await queuePrompt.apply(app, [...arguments]); } finally { that.processingQueue = false; - that.fireEvent('queue-end', {}); + that.dispatchEvent(new CustomEvent('queue-end')); } }; const graphToPrompt = app.graphToPrompt; app.graphToPrompt = async function () { - that.fireEvent('graph-to-prompt', {}); + that.dispatchEvent(new CustomEvent('graph-to-prompt')); let promise = graphToPrompt.apply(app, [...arguments]); await promise; - that.fireEvent('graph-to-prompt-end', {}); + that.dispatchEvent(new CustomEvent('graph-to-prompt-end')); return promise; }; const clean = app.clean; @@ -247,25 +266,6 @@ class Rgthree { link.href = 'extensions/rgthree-comfy/rgthree.css'; document.head.appendChild(link); } - addEventListener(event, fn) { - if (!this.eventsToFns.has(event)) { - this.eventsToFns.set(event, new Set()); - } - this.eventsToFns.get(event).add(fn); - } - removeEventListener(event, fn) { - if (this.eventsToFns.has(event)) { - this.eventsToFns.get(event).delete(fn); - } - } - fireEvent(event, data) { - if (this.eventsToFns.has(event)) { - for (let fn of this.eventsToFns.get(event)) { - const event = new Event(data); - fn(event); - } - } - } setLogLevel(level) { GLOBAL_LOG_LEVEL = level; } diff --git a/web/utils.js b/web/utils.js index 3b51799..5e0730e 100644 --- a/web/utils.js +++ b/web/utils.js @@ -253,7 +253,12 @@ export function addHelpMenuItem(node, content, menuOptions) { addMenuItemOnExtraMenuOptions(node, { name: "🛟 Node Help", callback: (node) => { - new RgthreeHelpDialog(node, content).show(); + if (node.showHelp) { + node.showHelp(); + } + else { + new RgthreeHelpDialog(node, content).show(); + } }, }, menuOptions, 'Properties Panel'); }