From cd24fcf120fa0996f95ae26e2820feb500224517 Mon Sep 17 00:00:00 2001 From: rgthree Date: Tue, 2 Jan 2024 22:24:15 -0500 Subject: [PATCH] Add ability to have a restriction of a maximum of (or always) one enabled widget in fast muter/bypasser. Fixes #87 --- ts/base_node_mode_changer.ts | 75 +++++++++++++++++++++++++++++------ ts/bypasser.ts | 4 +- ts/muter.ts | 4 +- web/base_node_mode_changer.js | 68 +++++++++++++++++++++++++------ web/bypasser.js | 4 +- web/muter.js | 4 +- 6 files changed, 126 insertions(+), 33 deletions(-) diff --git a/ts/base_node_mode_changer.ts b/ts/base_node_mode_changer.ts index 293536c..01a1d2f 100644 --- a/ts/base_node_mode_changer.ts +++ b/ts/base_node_mode_changer.ts @@ -3,7 +3,7 @@ import {app} from "../../scripts/app.js"; import { BaseAnyInputConnectedNode } from "./base_any_input_connected_node.js"; import { RgthreeBaseNode } from "./base_node.js"; -import type {LGraphNode as TLGraphNode, LiteGraph as TLiteGraph, IWidget} from './typings/litegraph.js'; +import type {LGraphNode as TLGraphNode, LiteGraph as TLiteGraph, IWidget, INodeInputSlot, INodeOutputSlot, LLink} from './typings/litegraph.js'; import { PassThroughFollowing } from "./utils.js"; import { wait } from "./shared_utils.js"; @@ -21,9 +21,19 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode { readonly modeOn: number = -1; readonly modeOff: number = -1; + static "@restriction" = { + type: "combo", + values: ["None", "Max one", "Always one"], + }; + + private stabilizedWidgetStates: boolean[] = []; + constructor(title?: string) { super(title); + this.properties = this.properties || {}; + this.properties['restriction'] = 'None'; + wait(10).then(() => { if (this.modeOn < 0 || this.modeOff < 0) { throw new Error('modeOn and modeOff must be overridden.'); @@ -33,6 +43,19 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode { } override handleLinkedNodesStabilization(linkedNodes: TLGraphNode[]) { + let restictToOne = this.properties?.['restriction']?.includes(' one'); + let oneIsOn = false; + console.log(this.stabilizedWidgetStates.join(', '), ' | ', this.widgets?.map(w => w.value).join(', ')) + if (restictToOne && this.stabilizedWidgetStates.length) { + // Find what changed.. because if a latter one changed, then we want to make that the newest. + for (const [index, state] of this.stabilizedWidgetStates.entries()) { + if (linkedNodes[index] && (linkedNodes[index]!.mode === this.modeOn) !== state) { + (this.widgets[index] as any).doModeChange(linkedNodes[index]!.mode === this.modeOn); + break; + } + } + } + this.stabilizedWidgetStates = []; for (const [index, node] of linkedNodes.entries()) { let widget = this.widgets && this.widgets[index]; if (!widget) { @@ -41,33 +64,59 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode { (this as any)._tempWidth = this.size[0]; widget = this.addWidget('toggle', '', false, '', {"on": 'yes', "off": 'no'}); } - node && this.setWidget(widget, node); + node && this.setWidget(widget, node, restictToOne && oneIsOn ? false : undefined); + oneIsOn = oneIsOn || widget.value; + this.stabilizedWidgetStates.push(widget.value); } if (this.widgets && this.widgets.length > linkedNodes.length) { this.widgets.length = linkedNodes.length } + // If we always need one, and none are on, then make the first one on. + if (this.properties?.['restriction'] === 'Always one' && !oneIsOn) { + (this.widgets[0] as any).doModeChange(true, true); + } } - setWidget(widget: IWidget, linkedNode: TLGraphNode) { - const off = linkedNode.mode === this.modeOff; + override onConnectionsChange(type: number, index: number, connected: boolean, linkInfo: LLink, ioSlot: (INodeOutputSlot | INodeInputSlot)) { + // We can clear out the stabilizedWidgetStates states since we've made an explicit change. + this.stabilizedWidgetStates = []; + super.onConnectionsChange && super.onConnectionsChange(type, index, connected, linkInfo, ioSlot); + } + + protected setWidget(widget: IWidget, linkedNode: TLGraphNode, forceValue?: boolean) { + const value = forceValue == null ? linkedNode.mode === this.modeOn : forceValue; widget.name = `Enable ${linkedNode.title}`; widget.options = {'on': 'yes', 'off': 'no'} - widget.value = !off; - (widget as any).doModeChange = (force?: boolean) => { - let off = force == null ? linkedNode.mode === this.modeOff : force; - linkedNode.mode = (off ? this.modeOn : this.modeOff) as 1 | 2 | 3 | 4; - widget!.value = off; + widget.value = value; + (widget as any).doModeChange = (forceValue?: boolean, skipOtherNodeCheck?: boolean) => { + let newValue = forceValue == null ? linkedNode.mode === this.modeOff : forceValue; + if (skipOtherNodeCheck !== true) { + if (newValue && this.properties?.['restriction']?.includes(' one')) { + for (const widget of this.widgets) { + (widget as any).doModeChange(false, true); + } + } else if (!newValue && this.properties?.['restriction'] === 'Always one') { + newValue = true; + } + } + linkedNode.mode = (newValue ? this.modeOn : this.modeOff) as 1 | 2 | 3 | 4; + widget.value = newValue; + // We can clear out the stabilizedWidgetStates states since we've made an explicit change. + this.stabilizedWidgetStates = []; } widget.callback = () => { (widget as any).doModeChange(); } + if (forceValue != null) { + linkedNode.mode = (forceValue ? this.modeOn : this.modeOff) as 1 | 2 | 3 | 4; + } } - forceWidgetOff(widget: IWidget) { - (widget as any).doModeChange(false); + forceWidgetOff(widget: IWidget, skipOtherNodeCheck?: boolean) { + (widget as any).doModeChange(false, skipOtherNodeCheck); } - forceWidgetOn(widget: IWidget) { - (widget as any).doModeChange(true); + forceWidgetOn(widget: IWidget, skipOtherNodeCheck?: boolean) { + (widget as any).doModeChange(true, skipOtherNodeCheck); } diff --git a/ts/bypasser.ts b/ts/bypasser.ts index 6dcbe80..d7f55ec 100644 --- a/ts/bypasser.ts +++ b/ts/bypasser.ts @@ -25,11 +25,11 @@ class BypasserNode extends BaseNodeModeChanger { override async handleAction(action: string) { if (action === 'Bypass all') { for (const widget of this.widgets) { - this.forceWidgetOff(widget); + this.forceWidgetOff(widget, true); } } else if (action === 'Enable all') { for (const widget of this.widgets) { - this.forceWidgetOn(widget); + this.forceWidgetOn(widget, true); } } } diff --git a/ts/muter.ts b/ts/muter.ts index 758c672..7131e32 100644 --- a/ts/muter.ts +++ b/ts/muter.ts @@ -24,11 +24,11 @@ class MuterNode extends BaseNodeModeChanger { override async handleAction(action: string) { if (action === 'Mute all') { for (const widget of this.widgets) { - this.forceWidgetOff(widget); + this.forceWidgetOff(widget, true); } } else if (action === 'Enable all') { for (const widget of this.widgets) { - this.forceWidgetOn(widget); + this.forceWidgetOn(widget, true); } } } diff --git a/web/base_node_mode_changer.js b/web/base_node_mode_changer.js index 2d1f8cc..f04258d 100644 --- a/web/base_node_mode_changer.js +++ b/web/base_node_mode_changer.js @@ -8,6 +8,9 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode { this.isVirtualNode = true; this.modeOn = -1; this.modeOff = -1; + this.stabilizedWidgetStates = []; + this.properties = this.properties || {}; + this.properties['restriction'] = 'None'; wait(10).then(() => { if (this.modeOn < 0 || this.modeOff < 0) { throw new Error('modeOn and modeOff must be overridden.'); @@ -16,40 +19,81 @@ export class BaseNodeModeChanger extends BaseAnyInputConnectedNode { this.addOutput("OPT_CONNECTION", "*"); } handleLinkedNodesStabilization(linkedNodes) { + var _a, _b, _c, _d; + let restictToOne = (_b = (_a = this.properties) === null || _a === void 0 ? void 0 : _a['restriction']) === null || _b === void 0 ? void 0 : _b.includes(' one'); + let oneIsOn = false; + console.log(this.stabilizedWidgetStates.join(', '), ' | ', (_c = this.widgets) === null || _c === void 0 ? void 0 : _c.map(w => w.value).join(', ')); + if (restictToOne && this.stabilizedWidgetStates.length) { + for (const [index, state] of this.stabilizedWidgetStates.entries()) { + if (linkedNodes[index] && (linkedNodes[index].mode === this.modeOn) !== state) { + this.widgets[index].doModeChange(linkedNodes[index].mode === this.modeOn); + break; + } + } + } + this.stabilizedWidgetStates = []; for (const [index, node] of linkedNodes.entries()) { let widget = this.widgets && this.widgets[index]; if (!widget) { this._tempWidth = this.size[0]; widget = this.addWidget('toggle', '', false, '', { "on": 'yes', "off": 'no' }); } - node && this.setWidget(widget, node); + node && this.setWidget(widget, node, restictToOne && oneIsOn ? false : undefined); + oneIsOn = oneIsOn || widget.value; + this.stabilizedWidgetStates.push(widget.value); } if (this.widgets && this.widgets.length > linkedNodes.length) { this.widgets.length = linkedNodes.length; } + if (((_d = this.properties) === null || _d === void 0 ? void 0 : _d['restriction']) === 'Always one' && !oneIsOn) { + this.widgets[0].doModeChange(true, true); + } } - setWidget(widget, linkedNode) { - const off = linkedNode.mode === this.modeOff; + onConnectionsChange(type, index, connected, linkInfo, ioSlot) { + this.stabilizedWidgetStates = []; + super.onConnectionsChange && super.onConnectionsChange(type, index, connected, linkInfo, ioSlot); + } + setWidget(widget, linkedNode, forceValue) { + const value = forceValue == null ? linkedNode.mode === this.modeOn : forceValue; widget.name = `Enable ${linkedNode.title}`; widget.options = { 'on': 'yes', 'off': 'no' }; - widget.value = !off; - widget.doModeChange = (force) => { - let off = force == null ? linkedNode.mode === this.modeOff : force; - linkedNode.mode = (off ? this.modeOn : this.modeOff); - widget.value = off; + widget.value = value; + widget.doModeChange = (forceValue, skipOtherNodeCheck) => { + var _a, _b, _c; + let newValue = forceValue == null ? linkedNode.mode === this.modeOff : forceValue; + if (skipOtherNodeCheck !== true) { + if (newValue && ((_b = (_a = this.properties) === null || _a === void 0 ? void 0 : _a['restriction']) === null || _b === void 0 ? void 0 : _b.includes(' one'))) { + for (const widget of this.widgets) { + widget.doModeChange(false, true); + } + } + else if (!newValue && ((_c = this.properties) === null || _c === void 0 ? void 0 : _c['restriction']) === 'Always one') { + newValue = true; + } + } + linkedNode.mode = (newValue ? this.modeOn : this.modeOff); + widget.value = newValue; + this.stabilizedWidgetStates = []; }; widget.callback = () => { widget.doModeChange(); }; + if (forceValue != null) { + linkedNode.mode = (forceValue ? this.modeOn : this.modeOff); + } } - forceWidgetOff(widget) { - widget.doModeChange(false); + forceWidgetOff(widget, skipOtherNodeCheck) { + widget.doModeChange(false, skipOtherNodeCheck); } - forceWidgetOn(widget) { - widget.doModeChange(true); + forceWidgetOn(widget, skipOtherNodeCheck) { + widget.doModeChange(true, skipOtherNodeCheck); } static setUp(clazz) { BaseAnyInputConnectedNode.setUp(clazz); } } BaseNodeModeChanger.collapsible = false; +BaseNodeModeChanger["@restriction"] = { + type: "combo", + values: ["None", "Max one", "Always one"], +}; diff --git a/web/bypasser.js b/web/bypasser.js index f919ff8..a53c2a5 100644 --- a/web/bypasser.js +++ b/web/bypasser.js @@ -12,12 +12,12 @@ class BypasserNode extends BaseNodeModeChanger { async handleAction(action) { if (action === 'Bypass all') { for (const widget of this.widgets) { - this.forceWidgetOff(widget); + this.forceWidgetOff(widget, true); } } else if (action === 'Enable all') { for (const widget of this.widgets) { - this.forceWidgetOn(widget); + this.forceWidgetOn(widget, true); } } } diff --git a/web/muter.js b/web/muter.js index dcb7470..65abd3e 100644 --- a/web/muter.js +++ b/web/muter.js @@ -12,12 +12,12 @@ class MuterNode extends BaseNodeModeChanger { async handleAction(action) { if (action === 'Mute all') { for (const widget of this.widgets) { - this.forceWidgetOff(widget); + this.forceWidgetOff(widget, true); } } else if (action === 'Enable all') { for (const widget of this.widgets) { - this.forceWidgetOn(widget); + this.forceWidgetOn(widget, true); } } }