From 583ecbba3e59791870b76a0b16417bb35af0cdfb Mon Sep 17 00:00:00 2001 From: "Alexander G. Morano" Date: Wed, 30 Apr 2025 23:19:44 -0400 Subject: [PATCH] unified widget_vector --- web/nodes/delay.js | 47 ++++++++- web/util/util.js | 157 ------------------------------ web/util/util_jov.js | 12 ++- web/util/util_widget.js | 8 +- web/{widget => }/widget_vector.js | 44 ++++++--- 5 files changed, 88 insertions(+), 180 deletions(-) delete mode 100644 web/util/util.js rename web/{widget => }/widget_vector.js (89%) diff --git a/web/nodes/delay.js b/web/nodes/delay.js index 1c71be4..5a1dc3b 100644 --- a/web/nodes/delay.js +++ b/web/nodes/delay.js @@ -2,7 +2,6 @@ import { api } from "../../../scripts/api.js"; import { app } from "../../../scripts/app.js"; -import { domShowModal } from '../util/util.js' import { apiJovimetrix } from '../util/util_jov.js' import { bubbles } from '../util/util_fun.js' @@ -10,6 +9,52 @@ const _id = "DELAY (JOV) โœ‹๐Ÿฝ" const EVENT_JOVI_DELAY = "jovi-delay-user"; const EVENT_JOVI_UPDATE = "jovi-delay-update"; +function domShowModal(innerHTML, eventCallback, timeout=null) { + return new Promise((resolve, reject) => { + const modal = document.createElement("div"); + modal.className = "modal"; + modal.innerHTML = innerHTML; + document.body.appendChild(modal); + + // center + const modalContent = modal.querySelector(".jov-modal-content"); + modalContent.style.position = "absolute"; + modalContent.style.left = "50%"; + modalContent.style.top = "50%"; + modalContent.style.transform = "translate(-50%, -50%)"; + + let timeoutId; + + const handleEvent = (event) => { + const targetId = event.target.id; + const result = eventCallback(targetId); + + if (result != null) { + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + modal.remove(); + resolve(result); + } + }; + modalContent.addEventListener("click", handleEvent); + modalContent.addEventListener("dblclick", handleEvent); + + if (timeout) { + timeout *= 1000; + timeoutId = setTimeout(() => { + modal.remove(); + reject(new Error("TIMEOUT")); + }, timeout); + } + + //setTimeout(() => { + // modal.dispatchEvent(new Event('tick')); + //}, 1000); + }); +} + app.registerExtension({ name: 'jovimetrix.node.' + _id, async beforeRegisterNodeDef(nodeType, nodeData) { diff --git a/web/util/util.js b/web/util/util.js deleted file mode 100644 index b9b26d6..0000000 --- a/web/util/util.js +++ /dev/null @@ -1,157 +0,0 @@ -/**/ - -import { app } from "../../../scripts/app.js" - -function arrayToObject(values, length, parseFn) { - const result = {}; - for (let i = 0; i < length; i++) { - result[i] = parseFn(values[i]); - } - return result; -} - -export function domRenderTemplate(template, data) { - for (const key in data) { - if (Object.prototype.hasOwnProperty.call(data, key)) { - const regex = new RegExp(`{{\\s*${key}\\s*}}`, 'g') - template = template.replace(regex, data[key]) - } - } - return template -} - -export function domFoldableToggle(elementId, symbolId) { - const content = document.getElementById(elementId) - const symbol = document.getElementById(symbolId) - if (content.style.display == 'none' || content.style.display == '') { - content.style.display = 'flex' - symbol.innerHTML = '▽' // Down arrow - } else { - content.style.display = 'none' - symbol.innerHTML = '▷' // Right arrow - } -} - -export function domInnerValueChange(node, pos, widget, value, event=undefined) { - const type = widget.type.includes("INT") ? Number : parseFloat - widget.value = arrayToObject(value, Object.keys(value).length, type); - if ( - widget.options && - widget.options.property && - node.properties[widget.options.property] !== undefined - ) { - node.setProperty(widget.options.property, widget.value) - } - if (widget.callback) { - - widget.callback(widget.value, app.canvas, node, pos, event) - } -} - -export function domWidgetOffset( - widget, - ctx, - node, - widgetWidth, - widgetY, - height - ) { - const margin = 10 - const elRect = ctx.canvas.getBoundingClientRect() - const transform = new DOMMatrix() - .scaleSelf( - elRect.width / ctx.canvas.width, - elRect.height / ctx.canvas.height - ) - .multiplySelf(ctx.getTransform()) - .translateSelf(0, widgetY + margin) - - const scale = new DOMMatrix().scaleSelf(transform.a, transform.d) - Object.assign(widget.inputEl.style, { - transformOrigin: '0 0', - transform: scale, - left: `${transform.e}px`, - top: `${transform.d + transform.f}px`, - width: `${widgetWidth}px`, - height: `${(height || widget.parent?.inputHeight || 32) - margin}px`, - position: 'absolute', - background: !node.color ? '' : node.color, - color: !node.color ? '' : 'white', - zIndex: 5, //app.graph._nodes.indexOf(node), - }) -} - -export function domEscapeHtml(unsafe) { - return unsafe - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, ''') -} - -export function domShowModal(innerHTML, eventCallback, timeout=null) { - return new Promise((resolve, reject) => { - const modal = document.createElement("div"); - modal.className = "modal"; - modal.innerHTML = innerHTML; - document.body.appendChild(modal); - - // center - const modalContent = modal.querySelector(".jov-modal-content"); - modalContent.style.position = "absolute"; - modalContent.style.left = "50%"; - modalContent.style.top = "50%"; - modalContent.style.transform = "translate(-50%, -50%)"; - - let timeoutId; - - const handleEvent = (event) => { - const targetId = event.target.id; - const result = eventCallback(targetId); - - if (result != null) { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - modal.remove(); - resolve(result); - } - }; - modalContent.addEventListener("click", handleEvent); - modalContent.addEventListener("dblclick", handleEvent); - - if (timeout) { - timeout *= 1000; - timeoutId = setTimeout(() => { - modal.remove(); - reject(new Error("TIMEOUT")); - }, timeout); - } - - //setTimeout(() => { - // modal.dispatchEvent(new Event('tick')); - //}, 1000); - }); -} - -/* -* Parse a string "255,255,255,255" or a List[255,255,255,255] into hex -*/ - -export function colorLerpHex(colorStart, colorEnd, lerp) { - // Parse color strings into RGB arrays - const startRGB = colorHex2RGB(colorStart); - const endRGB = colorHex2RGB(colorEnd); - - // Linearly interpolate each RGB component - const lerpedRGB = startRGB.map((component, index) => { - return Math.round(component + (endRGB[index] - component) * lerp); - }); - - // Convert the interpolated RGB values back to a hex color string - return colorRGB2Hex(lerpedRGB); -} - - diff --git a/web/util/util_jov.js b/web/util/util_jov.js index d4f337b..9eca8a2 100644 --- a/web/util/util_jov.js +++ b/web/util/util_jov.js @@ -125,15 +125,19 @@ export function widgetHookControl(node, control_key, target, matchFloatSize=fals combo.callback = () => { const me = oldCallback?.apply(this, arguments); widgetHide(node, target, "-jov"); - if (["VEC2", "VEC2INT", "COORD2D", "VEC3", "VEC3INT", "VEC4", "VEC4INT", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) { + //if (["VEC2", "VEC2INT", "COORD2D", "VEC3", "VEC3INT", "VEC4", "VEC4INT", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) { + if (["VEC2", "VEC3", "VEC4", "BOOLEAN", "INT", "FLOAT"].includes(combo.value)) { let type = combo.value; if (matchFloatSize) { type = "FLOAT"; - if (["VEC2", "VEC2INT", "COORD2D"].includes(combo.value)) { + // if (["VEC2", "VEC2INT", "COORD2D"].includes(combo.value)) { + if (["VEC2"].includes(combo.value)) { type = "VEC2"; - } else if (["VEC3", "VEC3INT"].includes(combo.value)) { + //} else if (["VEC3", "VEC3INT"].includes(combo.value)) { + } else if (["VEC3"].includes(combo.value)) { type = "VEC3"; - } else if (["VEC4", "VEC4INT"].includes(combo.value)) { + //} else if (["VEC4", "VEC4INT"].includes(combo.value)) { + } else if (["VEC4"].includes(combo.value)) { type = "VEC4"; } } diff --git a/web/util/util_widget.js b/web/util/util_widget.js index 26f8d5a..c6bec35 100644 --- a/web/util/util_widget.js +++ b/web/util/util_widget.js @@ -11,12 +11,12 @@ const _MAP = { INT: "๐Ÿ”Ÿ", FLOAT: "๐Ÿ›Ÿ", VEC2: "๐Ÿ‡ฝ๐Ÿ‡พ", - COORD2D: "๐Ÿ‡ฝ๐Ÿ‡พ", - VEC2INT: "๐Ÿ‡ฝ๐Ÿ‡พ", + //COORD2D: "๐Ÿ‡ฝ๐Ÿ‡พ", + //VEC2INT: "๐Ÿ‡ฝ๐Ÿ‡พ", VEC3: "๐Ÿ‡ฝ๐Ÿ‡พ\u200c๐Ÿ‡ฟ", - VEC3INT: "๐Ÿ‡ฝ๐Ÿ‡พ\u200c๐Ÿ‡ฟ", + //VEC3INT: "๐Ÿ‡ฝ๐Ÿ‡พ\u200c๐Ÿ‡ฟ", VEC4: "๐Ÿ‡ฝ๐Ÿ‡พ\u200c๐Ÿ‡ฟ\u200c๐Ÿ‡ผ", - VEC4INT: "๐Ÿ‡ฝ๐Ÿ‡พ\u200c๐Ÿ‡ฟ\u200c๐Ÿ‡ผ", + //VEC4INT: "๐Ÿ‡ฝ๐Ÿ‡พ\u200c๐Ÿ‡ฟ\u200c๐Ÿ‡ผ", LIST: "๐Ÿงพ", DICT: "๐Ÿ“–", IMAGE: "๐Ÿ–ผ๏ธ", diff --git a/web/widget/widget_vector.js b/web/widget_vector.js similarity index 89% rename from web/widget/widget_vector.js rename to web/widget_vector.js index fefbf75..b535909 100644 --- a/web/widget/widget_vector.js +++ b/web/widget_vector.js @@ -1,10 +1,32 @@ /**/ -import { app } from "../../../scripts/app.js" -import { $el } from "../../../scripts/ui.js" -import { domInnerValueChange } from '../util/util.js' +import { app } from "../../scripts/app.js" +import { $el } from "../../scripts/ui.js" /** @import { IWidget, LGraphCanvas } from '../../types/litegraph/litegraph.d.ts' */ +function arrayToObject(values, length, parseFn) { + const result = {}; + for (let i = 0; i < length; i++) { + result[i] = parseFn(values[i]); + } + return result; +} + +function domInnerValueChange(node, pos, widget, value, event=undefined) { + //const numtype = widget.type.includes("INT") ? Number : parseFloat + widget.value = arrayToObject(value, Object.keys(value).length, widget.convert); + if ( + widget.options && + widget.options.property && + node.properties[widget.options.property] !== undefined + ) { + node.setProperty(widget.options.property, widget.value) + } + if (widget.callback) { + widget.callback(widget.value, app.canvas, node, pos, event) + } +} + function colorHex2RGB(hex) { hex = hex.replace(/^#/, ''); const bigint = parseInt(hex, 16); @@ -32,13 +54,14 @@ const VectorWidget = (app, inputName, options, initial) => { /** @type {IWidget} */ const widget = { name: inputName, - type2: options[0], + //type2: options[0], type: options[0], y: 0, value: values, options: options[1] } + widget.convert = parseFloat; widget.options.precision = 3; widget.options.step = 0.01; widget.options.round = 1 / 10 ** widget.options.step; @@ -47,6 +70,7 @@ const VectorWidget = (app, inputName, options, initial) => { widget.options.step = 1; widget.options.round = 1; widget.options.precision = 0; + widget.convert = Number; } if (widget.options?.rgb || false) { @@ -65,7 +89,8 @@ const VectorWidget = (app, inputName, options, initial) => { let picker; widget.draw = function(ctx, node, width, Y, height) { - if ((app.canvas.ds.scale < 0.50) || (!this.type2.startsWith("VEC") && this.type2 != "COORD2D")) return; + // if ((app.canvas.ds.scale < 0.50) || (!this.type2.startsWith("VEC") && this.type2 != "COORD2D")) return; + if ((app.canvas.ds.scale < 0.50) || (!this.type.startsWith("VEC"))) return; ctx.save() ctx.beginPath() ctx.lineWidth = 1 @@ -269,15 +294,6 @@ app.registerExtension({ }), VEC4: (node, inputName, inputData, app) => ({ widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0, 0, 0])), - }), - VEC2INT: (node, inputName, inputData, app) => ({ - widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0])), - }), - VEC3INT: (node, inputName, inputData, app) => ({ - widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0, 0])), - }), - VEC4INT: (node, inputName, inputData, app) => ({ - widget: node.addCustomWidget(VectorWidget(app, inputName, inputData, [0, 0, 0, 0])), }) } }