When generating certain images or videos you need a certain divisible by number such as 16 or it throws an error. These changes enforce the step value upon the min and max values to the nearest step and round of the free enter from the pencil to the nearest step.
1170 lines
47 KiB
JavaScript
1170 lines
47 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
import { SettingsPopup } from "./AIDocSharedClasses.js";
|
|
|
|
function calculateLuminance(hexColor) {
|
|
hexColor = hexColor.replace(/^#/, '');
|
|
const r = parseInt(hexColor.substring(0, 2), 16) / 255;
|
|
const g = parseInt(hexColor.substring(2, 4), 16) / 255;
|
|
const b = parseInt(hexColor.substring(4, 6), 16) / 255;
|
|
const a = [r, g, b].map(function (x) {
|
|
return (x <= 0.03928) ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
|
|
});
|
|
return (a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722);
|
|
}
|
|
|
|
function getTextColorForBackground(bgColor) {
|
|
const luminance = calculateLuminance(bgColor);
|
|
return luminance < 0.5 ? '#ffffff' : '#000000';
|
|
}
|
|
|
|
function truncateText(ctx, text, maxWidth) {
|
|
const ellipsis = "...";
|
|
const ellipsisWidth = ctx.measureText(ellipsis).width;
|
|
if (ctx.measureText(text).width <= maxWidth) {
|
|
return text;
|
|
}
|
|
let truncated = text;
|
|
while (truncated && ctx.measureText(truncated).width + ellipsisWidth > maxWidth) {
|
|
truncated = truncated.slice(0, -1);
|
|
}
|
|
return truncated + ellipsis;
|
|
}
|
|
|
|
class MultiIntNode {
|
|
constructor(node) {
|
|
this.node = node;
|
|
if (typeof node.properties.activeCount !== "number") {
|
|
node.properties.activeCount = 20;
|
|
}
|
|
if (!Array.isArray(node.properties.intSteps)) {
|
|
node.properties.intSteps = [];
|
|
}
|
|
if (!Array.isArray(node.properties.labels)) {
|
|
node.properties.labels = [];
|
|
}
|
|
if (!Array.isArray(node.properties.values)) {
|
|
node.properties.values = [];
|
|
}
|
|
if (!Array.isArray(node.properties.intMin)) {
|
|
node.properties.intMin = [];
|
|
}
|
|
if (!Array.isArray(node.properties.intMax)) {
|
|
node.properties.intMax = [];
|
|
}
|
|
this.adjustArrays();
|
|
|
|
for (let i = 0; i < this.node.properties.activeCount; i++) {
|
|
const stepVal = this.node.properties.intSteps[i] || 1;
|
|
|
|
if (typeof this.node.properties.intMin[i] === "number") {
|
|
const oldMin = this.node.properties.intMin[i];
|
|
const newMin = stepVal * Math.round(oldMin / stepVal);
|
|
this.node.properties.intMin[i] = newMin;
|
|
}
|
|
if (typeof this.node.properties.intMax[i] === "number") {
|
|
const oldMax = this.node.properties.intMax[i];
|
|
const newMax = stepVal * Math.round(oldMax / stepVal);
|
|
this.node.properties.intMax[i] = newMax;
|
|
}
|
|
}
|
|
|
|
if (typeof node.properties.bottomPadding !== "number") {
|
|
node.properties.bottomPadding = 0;
|
|
}
|
|
if (typeof node.properties.minWidth !== "number") {
|
|
node.properties.minWidth = 250;
|
|
}
|
|
if (typeof node.properties.maxWidth !== "number") {
|
|
node.properties.maxWidth = 1000;
|
|
}
|
|
|
|
this.labelRects = [];
|
|
this.valueRects = [];
|
|
this.pencilRects = [];
|
|
this.incrementRects = [];
|
|
this.decrementRects = [];
|
|
this.gearRect = null;
|
|
|
|
this.setupOutputsAndWidgets();
|
|
node.setDirtyCanvas(true, true);
|
|
node.onDrawForeground = this.onDrawForeground.bind(this);
|
|
}
|
|
|
|
setupOutputsAndWidgets() {
|
|
const oldSize = [...this.node.size];
|
|
this.node.outputs = [];
|
|
this.node.widgets = [];
|
|
for (let i = 0; i < this.node.properties.activeCount; i++) {
|
|
this.node.addOutput("", "INT");
|
|
const w = this.node.addWidget(
|
|
"number",
|
|
`value${i+1}`,
|
|
this.node.properties.values[i],
|
|
(val) => this.onValueChanged(i, val),
|
|
{ hidden: true }
|
|
);
|
|
w.computeSize = () => [0, -4];
|
|
w.hidden = true;
|
|
}
|
|
for (let i = 0; i < this.node.outputs.length; i++) {
|
|
this.node.outputs[i].name = `i${i+1}`;
|
|
}
|
|
this.node.size[0] = oldSize[0];
|
|
}
|
|
|
|
onDrawForeground(ctx) {
|
|
if (this.node.flags.collapsed) return;
|
|
if (this.node.outputs.length !== this.node.properties.activeCount) {
|
|
this.node.outputs = [];
|
|
this.node.widgets = [];
|
|
this.setupOutputsAndWidgets();
|
|
if (this.node.onOutputsChange) {
|
|
this.node.onOutputsChange();
|
|
}
|
|
}
|
|
|
|
const margin = 8;
|
|
const rowHeight = 30;
|
|
const rowSpacing = margin;
|
|
const count = this.node.properties.activeCount;
|
|
const neededHeight = margin + count * (rowHeight + rowSpacing) + (this.node.properties.bottomPadding || 0);
|
|
if (this.node.size[1] < neededHeight) {
|
|
this.node.size[1] = neededHeight;
|
|
} else if (this.node.size[1] > neededHeight) {
|
|
this.node.size[1] = neededHeight;
|
|
}
|
|
if (this.node.size[0] < this.node.properties.minWidth) {
|
|
this.node.size[0] = this.node.properties.minWidth;
|
|
}
|
|
if (this.node.size[0] > this.node.properties.maxWidth) {
|
|
this.node.size[0] = this.node.properties.maxWidth;
|
|
}
|
|
|
|
const bgColor = this.node.properties.bgColor || "#181818";
|
|
ctx.fillStyle = bgColor;
|
|
const radius = 8;
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, 0);
|
|
ctx.lineTo(this.node.size[0], 0);
|
|
ctx.lineTo(this.node.size[0], this.node.size[1] - radius);
|
|
ctx.arcTo(this.node.size[0], this.node.size[1], this.node.size[0] - radius, this.node.size[1], radius);
|
|
ctx.lineTo(radius, this.node.size[1]);
|
|
ctx.arcTo(0, this.node.size[1], 0, this.node.size[1] - radius, radius);
|
|
ctx.lineTo(0, radius);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
const rowBgWidth = this.node.size[0] - margin - 38;
|
|
const rowBgColor = this.node.properties.rowBgColor || "#121212";
|
|
const rowTextColor = getTextColorForBackground(rowBgColor) || "#fff";
|
|
|
|
this.labelRects = [];
|
|
this.valueRects = [];
|
|
this.pencilRects = [];
|
|
this.incrementRects = [];
|
|
this.decrementRects = [];
|
|
|
|
let y = margin;
|
|
for (let i = 0; i < count; i++) {
|
|
ctx.fillStyle = rowBgColor;
|
|
ctx.beginPath();
|
|
ctx.moveTo(margin + radius, y);
|
|
ctx.arcTo(margin + rowBgWidth, y, margin + rowBgWidth, y + rowHeight, radius);
|
|
ctx.arcTo(margin + rowBgWidth, y + rowHeight, margin, y + rowHeight, radius);
|
|
ctx.arcTo(margin, y + rowHeight, margin, y, radius);
|
|
ctx.arcTo(margin, y, margin + rowBgWidth, y, radius);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
ctx.fillStyle = rowTextColor;
|
|
ctx.font = "12px Arial";
|
|
ctx.textBaseline = "middle";
|
|
ctx.textAlign = "left";
|
|
const staticLabel = `·i${i + 1}`;
|
|
const staticLabelMetrics = ctx.measureText(staticLabel);
|
|
const staticLabelX = margin + 2;
|
|
const staticLabelY = y + rowHeight / 2;
|
|
ctx.fillText(staticLabel, staticLabelX, staticLabelY);
|
|
|
|
const lineStartX = staticLabelX + staticLabelMetrics.width + 4;
|
|
ctx.strokeStyle = rowTextColor;
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.moveTo(lineStartX, staticLabelY - rowHeight / 2);
|
|
ctx.lineTo(lineStartX, staticLabelY + rowHeight / 2);
|
|
ctx.stroke();
|
|
|
|
const valX = margin + rowBgWidth - 90;
|
|
const valY = staticLabelY;
|
|
|
|
const labelAreaStart = lineStartX + 6;
|
|
const labelAreaEnd = valX - 80;
|
|
let availableWidth = labelAreaEnd - labelAreaStart;
|
|
if (availableWidth < 0) {
|
|
availableWidth = 0;
|
|
}
|
|
|
|
const labelWidthRules = [
|
|
{ max: 10, set: 60 },
|
|
{ max: 15, set: 65 },
|
|
{ max: 30, set: 80 },
|
|
{ max: 50, set: 90 },
|
|
{ max: 70, set: 100 },
|
|
{ max: 90, set: 110 },
|
|
{ max: 150, multiplier: 1.2 },
|
|
{ max: 200, multiplier: 1.15 },
|
|
{ max: 400, multiplier: 0.98 },
|
|
{ max: 500, multiplier: 0.95 },
|
|
{ max: Infinity, multiplier: 0.93 }
|
|
];
|
|
function adjustLabelWidth(width) {
|
|
for (const rule of labelWidthRules) {
|
|
if (width <= rule.max) {
|
|
if (rule.set !== undefined) {
|
|
return rule.set;
|
|
}
|
|
if (rule.multiplier !== undefined) {
|
|
return width * rule.multiplier;
|
|
}
|
|
}
|
|
}
|
|
return width;
|
|
}
|
|
const customLabelRaw = this.node.properties.labels[i] || `INT ${i + 1}`;
|
|
const adjustedWidth = adjustLabelWidth(availableWidth);
|
|
const displayLabel = truncateText(ctx, customLabelRaw, adjustedWidth);
|
|
|
|
ctx.fillStyle = rowTextColor;
|
|
ctx.font = "14px Arial";
|
|
ctx.fillText(displayLabel, labelAreaStart, staticLabelY);
|
|
|
|
const displayedLabelWidth = ctx.measureText(displayLabel).width;
|
|
this.labelRects[i] = {
|
|
x: labelAreaStart,
|
|
y,
|
|
w: displayedLabelWidth + 8,
|
|
h: rowHeight
|
|
};
|
|
|
|
const val = this.node.properties.values[i] ?? 0;
|
|
const valStr = val.toString();
|
|
ctx.textAlign = "right";
|
|
ctx.font = "14px Arial";
|
|
const valMetrics = ctx.measureText(valStr);
|
|
ctx.fillText(valStr, valX, valY);
|
|
this.valueRects[i] = {
|
|
x: valX - valMetrics.width - 4,
|
|
y,
|
|
w: valMetrics.width + 8,
|
|
h: rowHeight
|
|
};
|
|
|
|
ctx.textAlign = "left";
|
|
ctx.fillStyle = rowTextColor;
|
|
ctx.font = "16px Arial";
|
|
const pencilIcon = "✎";
|
|
const pencilMetrics = ctx.measureText(pencilIcon);
|
|
const pencilX = valX + 6;
|
|
ctx.fillText(pencilIcon, pencilX, valY);
|
|
this.pencilRects[i] = {
|
|
x: pencilX,
|
|
y,
|
|
w: pencilMetrics.width + 4,
|
|
h: rowHeight
|
|
};
|
|
|
|
const incDecColor = this.node.properties.buttonColor || "#4b3e72";
|
|
const incDecTextColor = getTextColorForBackground(incDecColor);
|
|
const decrementButtonBoxX = valX + 36;
|
|
const decrementButtonBoxY = y + (rowHeight / 4) - 2;
|
|
const buttonBoxWidth = 20;
|
|
const buttonBoxHeight = 20;
|
|
const incrementButtonBoxX = decrementButtonBoxX + 24;
|
|
const incrementButtonBoxY = decrementButtonBoxY;
|
|
|
|
ctx.fillStyle = incDecColor;
|
|
ctx.beginPath();
|
|
ctx.moveTo(decrementButtonBoxX + 5, decrementButtonBoxY);
|
|
ctx.arcTo(decrementButtonBoxX + buttonBoxWidth, decrementButtonBoxY, decrementButtonBoxX + buttonBoxWidth, decrementButtonBoxY + buttonBoxHeight, 5);
|
|
ctx.arcTo(decrementButtonBoxX + buttonBoxWidth, decrementButtonBoxY + buttonBoxHeight, decrementButtonBoxX, decrementButtonBoxY + buttonBoxHeight, 5);
|
|
ctx.arcTo(decrementButtonBoxX, decrementButtonBoxY + buttonBoxHeight, decrementButtonBoxX, decrementButtonBoxY, 5);
|
|
ctx.arcTo(decrementButtonBoxX, decrementButtonBoxY, decrementButtonBoxX + buttonBoxWidth, decrementButtonBoxY, 5);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
ctx.font = "12px Arial";
|
|
ctx.fillStyle = incDecTextColor;
|
|
ctx.fillText("-", decrementButtonBoxX + 8, decrementButtonBoxY + buttonBoxHeight / 2);
|
|
this.decrementRects[i] = {
|
|
x: decrementButtonBoxX,
|
|
y: decrementButtonBoxY,
|
|
w: buttonBoxWidth,
|
|
h: buttonBoxHeight
|
|
};
|
|
|
|
ctx.fillStyle = incDecColor;
|
|
ctx.beginPath();
|
|
ctx.moveTo(incrementButtonBoxX + 5, incrementButtonBoxY);
|
|
ctx.arcTo(incrementButtonBoxX + buttonBoxWidth, incrementButtonBoxY, incrementButtonBoxX + buttonBoxWidth, incrementButtonBoxY + buttonBoxHeight, 5);
|
|
ctx.arcTo(incrementButtonBoxX + buttonBoxWidth, incrementButtonBoxY + buttonBoxHeight, incrementButtonBoxX, incrementButtonBoxY + buttonBoxHeight, 5);
|
|
ctx.arcTo(incrementButtonBoxX, incrementButtonBoxY + buttonBoxHeight, incrementButtonBoxX, incrementButtonBoxY, 5);
|
|
ctx.arcTo(incrementButtonBoxX, incrementButtonBoxY, incrementButtonBoxX + buttonBoxWidth, incrementButtonBoxY, 5);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
ctx.font = "12px Arial";
|
|
ctx.fillStyle = incDecTextColor;
|
|
ctx.fillText("+", incrementButtonBoxX + 6, incrementButtonBoxY + buttonBoxHeight / 2);
|
|
this.incrementRects[i] = {
|
|
x: incrementButtonBoxX,
|
|
y: incrementButtonBoxY,
|
|
w: buttonBoxWidth,
|
|
h: buttonBoxHeight
|
|
};
|
|
|
|
y += rowHeight + rowSpacing;
|
|
}
|
|
|
|
const gearIcon = "⚙️";
|
|
ctx.font = "18px Arial";
|
|
ctx.textAlign = "center";
|
|
ctx.textBaseline = "middle";
|
|
ctx.fillStyle = "#ccc";
|
|
const gearWidth = ctx.measureText(gearIcon).width;
|
|
const gearHeight = 18;
|
|
const gearX = this.node.size[0] - margin - (gearWidth / 2);
|
|
const gearY = this.node.size[1] - margin - (gearHeight / 2);
|
|
ctx.fillText(gearIcon, gearX, gearY);
|
|
this.gearRect = {
|
|
x: gearX - gearWidth / 2,
|
|
y: gearY - gearHeight / 2,
|
|
w: gearWidth,
|
|
h: gearHeight
|
|
};
|
|
|
|
const bottomPad = parseInt(this.node.properties.bottomPadding, 10) || 0;
|
|
if (bottomPad > 0) {
|
|
ctx.fillStyle = "#181818";
|
|
ctx.fillRect(margin + radius, y, rowBgWidth, bottomPad);
|
|
y += bottomPad;
|
|
}
|
|
}
|
|
|
|
clampValue(index, val) {
|
|
const minVal = this.node.properties.intMin[index];
|
|
const maxVal = this.node.properties.intMax[index];
|
|
if (typeof minVal === "number" && val < minVal) {
|
|
val = minVal;
|
|
}
|
|
if (typeof maxVal === "number" && val > maxVal) {
|
|
val = maxVal;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
startValueDragToggle(index, initialEvent) {
|
|
if (this.dragActive) {
|
|
this.stopValueDrag();
|
|
return;
|
|
}
|
|
this.dragActive = true;
|
|
this.dragIndex = index;
|
|
this.dragInitialX = initialEvent.clientX;
|
|
this.dragInitialValue = this.node.properties.values[index];
|
|
const step = (this.node.properties.intSteps && this.node.properties.intSteps[index]) || 1;
|
|
const sensitivity = 10;
|
|
const canvasEl = document.getElementById("graph-canvas");
|
|
if (!canvasEl) return;
|
|
this._onValuePointerMove = (e) => {
|
|
const dx = e.clientX - this.dragInitialX;
|
|
const steps = Math.floor(dx / sensitivity);
|
|
let newValue = this.dragInitialValue + steps * step;
|
|
newValue = this.clampValue(index, newValue);
|
|
if (newValue !== this.node.properties.values[this.dragIndex]) {
|
|
this.node.properties.values[this.dragIndex] = newValue;
|
|
if (this.node.widgets[this.dragIndex]) {
|
|
this.node.widgets[this.dragIndex].value = newValue;
|
|
}
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
};
|
|
canvasEl.addEventListener("pointermove", this._onValuePointerMove);
|
|
this._onGlobalClickToStop = () => {
|
|
this.stopValueDrag();
|
|
};
|
|
document.addEventListener("click", this._onGlobalClickToStop, { capture: true });
|
|
this._onKeyDownToStop = (e) => {
|
|
if (e.key === "Enter") {
|
|
this.stopValueDrag();
|
|
}
|
|
};
|
|
document.addEventListener("keydown", this._onKeyDownToStop);
|
|
}
|
|
|
|
stopValueDrag() {
|
|
const canvasEl = document.getElementById("graph-canvas");
|
|
if (!canvasEl) return;
|
|
if (this._onValuePointerMove) {
|
|
canvasEl.removeEventListener("pointermove", this._onValuePointerMove);
|
|
this._onValuePointerMove = null;
|
|
}
|
|
if (this._onGlobalClickToStop) {
|
|
document.removeEventListener("click", this._onGlobalClickToStop, { capture: true });
|
|
this._onGlobalClickToStop = null;
|
|
}
|
|
if (this._onKeyDownToStop) {
|
|
document.removeEventListener("keydown", this._onKeyDownToStop);
|
|
this._onKeyDownToStop = null;
|
|
}
|
|
this.dragActive = false;
|
|
this.dragIndex = null;
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
|
|
onMouseDown(e, pos) {
|
|
for (let i = 0; i < this.pencilRects.length; i++) {
|
|
const r = this.pencilRects[i];
|
|
if (!r) continue;
|
|
if (pos[0] >= r.x && pos[0] <= r.x + r.w &&
|
|
pos[1] >= r.y && pos[1] <= r.y + r.h) {
|
|
const centerX = r.x + (r.w / 2);
|
|
const centerY = r.y + (r.h / 2);
|
|
this.showInlineIntEditor(i, { x: centerX, y: centerY, w: 80, h: 24 }, e);
|
|
return true;
|
|
}
|
|
}
|
|
for (let i = 0; i < this.labelRects.length; i++) {
|
|
const r = this.labelRects[i];
|
|
if (!r) continue;
|
|
if (pos[0] >= r.x && pos[0] <= r.x + r.w &&
|
|
pos[1] >= r.y && pos[1] <= r.y + r.h) {
|
|
const centerX = r.x + (r.w / 2);
|
|
const centerY = r.y + (r.h / 2);
|
|
this.showInlineLabelEditor(i, { x: centerX, y: centerY, w: 80, h: 24 }, e);
|
|
return true;
|
|
}
|
|
}
|
|
for (let i = 0; i < this.incrementRects.length; i++) {
|
|
const r = this.incrementRects[i];
|
|
if (pos[0] >= r.x && pos[0] <= r.x + r.w &&
|
|
pos[1] >= r.y && pos[1] <= r.y + r.h) {
|
|
this.incrementValue(i);
|
|
return true;
|
|
}
|
|
}
|
|
for (let i = 0; i < this.decrementRects.length; i++) {
|
|
const r = this.decrementRects[i];
|
|
if (pos[0] >= r.x && pos[0] <= r.x + r.w &&
|
|
pos[1] >= r.y && pos[1] <= r.y + r.h) {
|
|
this.decrementValue(i);
|
|
return true;
|
|
}
|
|
}
|
|
for (let i = 0; i < this.valueRects.length; i++) {
|
|
const r = this.valueRects[i];
|
|
if (r && pos[0] >= r.x && pos[0] <= r.x + r.w &&
|
|
pos[1] >= r.y && pos[1] <= r.y + r.h) {
|
|
this.startValueDragToggle(i, e);
|
|
return true;
|
|
}
|
|
}
|
|
if (this.gearRect) {
|
|
const r = this.gearRect;
|
|
if (pos[0] >= r.x && pos[0] <= r.x + r.w &&
|
|
pos[1] >= r.y && pos[1] <= r.y + r.h) {
|
|
this.openGearPopup(e);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
showInlineLabelEditor(index, rect, e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const graphCanvas = app.graphcanvas;
|
|
if (!graphCanvas) {
|
|
const fallbackX = e.clientX - (rect.w * 0.5);
|
|
const fallbackY = e.clientY - (rect.h * 0.5);
|
|
return this.spawnInlineLabelEditorAtScreenPos(index, fallbackX, fallbackY, rect);
|
|
}
|
|
const nodeX = this.node.pos[0];
|
|
const nodeY = this.node.pos[1];
|
|
const localX = rect.x + (rect.w * 0.5);
|
|
const localY = rect.y + (rect.h * 0.5);
|
|
const scale = graphCanvas.ds.scale;
|
|
const offset = graphCanvas.ds.offset;
|
|
const canvasX = (nodeX + localX) * scale + offset[0];
|
|
const canvasY = (nodeY + localY) * scale + offset[1];
|
|
const canvasRect = graphCanvas.canvas.getBoundingClientRect();
|
|
const screenX = canvasRect.left + canvasX;
|
|
const screenY = canvasRect.top + canvasY;
|
|
this.spawnInlineLabelEditorAtScreenPos(index, screenX, screenY, rect);
|
|
}
|
|
|
|
spawnInlineLabelEditorAtScreenPos(index, screenX, screenY, rect) {
|
|
const input = document.createElement("input");
|
|
input.type = "text";
|
|
input.value = this.node.properties.labels[index] || `INT ${index + 1}`;
|
|
input.style.position = "fixed";
|
|
const offsetX = -(rect.w * 0.5);
|
|
const offsetY = -(rect.h * 0.5);
|
|
const FixedOffsetX = 35;
|
|
const FixedOffsetY = 10;
|
|
input.style.left = (screenX + offsetX + FixedOffsetX) + "px";
|
|
input.style.top = (screenY + offsetY + FixedOffsetY) + "px";
|
|
input.style.width = Math.max(rect.w, 250) + "px";
|
|
input.style.height = Math.max(rect.h, 30) + "px";
|
|
input.style.fontSize = "14px";
|
|
input.style.zIndex = "10000";
|
|
input.style.background = "#181818";
|
|
input.style.color = "#fff";
|
|
input.style.border = "2px solid #4b3e72";
|
|
input.style.borderRadius = "6px";
|
|
input.style.outline = "none";
|
|
input.style.padding = "0 6px";
|
|
document.body.appendChild(input);
|
|
setTimeout(() => {
|
|
input.focus();
|
|
input.select();
|
|
}, 10);
|
|
const commit = () => {
|
|
let val = input.value.trim();
|
|
if (val.length > 120) {
|
|
val = val.substring(0, 120);
|
|
}
|
|
if (val.length > 0) {
|
|
this.node.properties.labels[index] = val;
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
remove();
|
|
};
|
|
const remove = () => {
|
|
document.removeEventListener("click", outsideClick, true);
|
|
if (input.parentNode) {
|
|
input.parentNode.removeChild(input);
|
|
}
|
|
};
|
|
const outsideClick = (evt) => {
|
|
if (evt.target !== input) {
|
|
commit();
|
|
}
|
|
};
|
|
setTimeout(() => {
|
|
document.addEventListener("click", outsideClick, true);
|
|
}, 100);
|
|
input.addEventListener("keydown", (evt) => {
|
|
if (evt.key === "Enter") {
|
|
evt.preventDefault();
|
|
commit();
|
|
} else if (evt.key === "Escape") {
|
|
evt.preventDefault();
|
|
remove();
|
|
}
|
|
});
|
|
}
|
|
|
|
openGearPopup(e) {
|
|
e.stopPropagation();
|
|
// Before actually showing the popup, force min/max to resnap to step.
|
|
for (let i = 0; i < this.node.properties.activeCount; i++) {
|
|
const stepVal = this.node.properties.intSteps[i] || 1;
|
|
if (typeof this.node.properties.intMin[i] === "number") {
|
|
const oldMin = this.node.properties.intMin[i];
|
|
const snappedMin = stepVal * Math.round(oldMin / stepVal);
|
|
this.node.properties.intMin[i] = snappedMin;
|
|
}
|
|
if (typeof this.node.properties.intMax[i] === "number") {
|
|
const oldMax = this.node.properties.intMax[i];
|
|
const snappedMax = stepVal * Math.round(oldMax / stepVal);
|
|
this.node.properties.intMax[i] = snappedMax;
|
|
}
|
|
}
|
|
let nodeElem = this.node.htmlElement;
|
|
if (!nodeElem) {
|
|
this.createGearPopupFallback(e.clientX, e.clientY);
|
|
return;
|
|
}
|
|
const nodeRect = nodeElem.getBoundingClientRect();
|
|
const popupX = nodeRect.left + 10;
|
|
const popupY = nodeRect.top + 10;
|
|
const popup = new SettingsPopup(this.node);
|
|
popup.open(popupX, popupY);
|
|
this.populateGearPopup(popup);
|
|
}
|
|
|
|
populateGearPopup(popup) {
|
|
const content = popup.getContentContainer();
|
|
popup.clearContent();
|
|
content.style.maxHeight = "300px";
|
|
content.style.overflowY = "auto";
|
|
content.style.scrollbarWidth = "thin";
|
|
content.style.setProperty("::-webkit-scrollbar", "width: 6px; height: 6px;");
|
|
content.style.setProperty("::-webkit-scrollbar-thumb", "background-color: #4b3e72; border-radius: 10px;");
|
|
content.style.setProperty("::-webkit-scrollbar-track", "background-color: #181818; border-radius: 10px;");
|
|
|
|
const addRemoveLabel = document.createElement("div");
|
|
addRemoveLabel.textContent = "Add/Remove Ints";
|
|
addRemoveLabel.style.color = "#fff";
|
|
addRemoveLabel.style.fontSize = "12px";
|
|
addRemoveLabel.style.textAlign = "center";
|
|
content.appendChild(addRemoveLabel);
|
|
|
|
const buttonRow = document.createElement("div");
|
|
buttonRow.style.display = "flex";
|
|
buttonRow.style.justifyContent = "center";
|
|
buttonRow.style.alignItems = "center";
|
|
buttonRow.style.width = "100%";
|
|
buttonRow.style.gap = "10px";
|
|
buttonRow.style.marginTop = "-5px";
|
|
content.appendChild(buttonRow);
|
|
|
|
const minusBtn = document.createElement("button");
|
|
minusBtn.textContent = "-";
|
|
minusBtn.style.background = "#333";
|
|
minusBtn.style.color = "#fff";
|
|
minusBtn.style.padding = "0px 12px";
|
|
minusBtn.style.fontSize = "16px";
|
|
buttonRow.appendChild(minusBtn);
|
|
|
|
const plusBtn = document.createElement("button");
|
|
plusBtn.textContent = "+";
|
|
plusBtn.style.background = "#333";
|
|
plusBtn.style.color = "#fff";
|
|
plusBtn.style.padding = "0px 8px";
|
|
plusBtn.style.fontSize = "16px";
|
|
buttonRow.appendChild(plusBtn);
|
|
|
|
const stepsLabel = document.createElement("div");
|
|
stepsLabel.textContent = "Steps / Min / Max";
|
|
stepsLabel.style.color = "#fff";
|
|
stepsLabel.style.fontSize = "12px";
|
|
stepsLabel.style.textAlign = "center";
|
|
content.appendChild(stepsLabel);
|
|
|
|
const stepsContainer = document.createElement("div");
|
|
stepsContainer.style.cssText = `
|
|
margin-top:-5px;
|
|
background: #131313;
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
min-height: 100px;
|
|
width: 95%;
|
|
overflow-y: auto;
|
|
scrollbar-width: thin;
|
|
`;
|
|
stepsContainer.style.setProperty("::-webkit-scrollbar", "width: 6px; height: 6px;");
|
|
stepsContainer.style.setProperty("::-webkit-scrollbar-thumb", "background-color: #4b3e72; border-radius: 10px;");
|
|
stepsContainer.style.setProperty("::-webkit-scrollbar-track", "background-color: #181818; border-radius: 10px;");
|
|
content.appendChild(stepsContainer);
|
|
|
|
const stepsList = document.createElement("div");
|
|
stepsContainer.appendChild(stepsList);
|
|
|
|
const updateStepInputs = () => {
|
|
stepsList.innerHTML = '';
|
|
|
|
for (let i = 0; i < this.node.properties.activeCount; i++) {
|
|
// A row for iN
|
|
const rowDiv = document.createElement("div");
|
|
rowDiv.style.display = "flex";
|
|
rowDiv.style.justifyContent = "center";
|
|
rowDiv.style.alignItems = "center";
|
|
rowDiv.style.marginBottom = "5px";
|
|
rowDiv.style.paddingBottom = "2px";
|
|
rowDiv.style.borderBottom = "2px solid #262626";
|
|
|
|
const intLabel = document.createElement("span");
|
|
intLabel.textContent = `i${i + 1}`;
|
|
intLabel.style.color = "#fff";
|
|
intLabel.style.fontSize = "14px";
|
|
intLabel.style.marginRight = "5px";
|
|
rowDiv.appendChild(intLabel);
|
|
|
|
// STEP input
|
|
const stepInput = document.createElement("input");
|
|
stepInput.type = "number";
|
|
stepInput.value = this.node.properties.intSteps[i] || 1;
|
|
stepInput.min = "1";
|
|
stepInput.style.width = "50px";
|
|
stepInput.style.fontSize = "12px";
|
|
stepInput.style.background = "#333";
|
|
stepInput.style.color = "#fff";
|
|
stepInput.style.border = "2px solid #4b3e72";
|
|
stepInput.style.borderRadius = "6px";
|
|
stepInput.style.textAlign = "center";
|
|
stepInput.style.marginRight = "5px";
|
|
rowDiv.appendChild(stepInput);
|
|
|
|
// MIN input
|
|
const minInput = document.createElement("input");
|
|
minInput.type = "number";
|
|
minInput.placeholder = "Min";
|
|
minInput.style.width = "50px";
|
|
minInput.style.fontSize = "12px";
|
|
minInput.style.background = "#333";
|
|
minInput.style.color = "#fff";
|
|
minInput.style.border = "2px solid #4b3e72";
|
|
minInput.style.borderRadius = "6px";
|
|
minInput.style.textAlign = "center";
|
|
minInput.style.marginRight = "5px";
|
|
if (typeof this.node.properties.intMin[i] === "number") {
|
|
minInput.value = this.node.properties.intMin[i];
|
|
} else {
|
|
minInput.value = "";
|
|
}
|
|
rowDiv.appendChild(minInput);
|
|
|
|
// MAX input
|
|
const maxInput = document.createElement("input");
|
|
maxInput.type = "number";
|
|
maxInput.placeholder = "Max";
|
|
maxInput.style.width = "50px";
|
|
maxInput.style.fontSize = "12px";
|
|
maxInput.style.background = "#333";
|
|
maxInput.style.color = "#fff";
|
|
maxInput.style.border = "2px solid #4b3e72";
|
|
maxInput.style.borderRadius = "6px";
|
|
maxInput.style.textAlign = "center";
|
|
if (typeof this.node.properties.intMax[i] === "number") {
|
|
maxInput.value = this.node.properties.intMax[i];
|
|
} else {
|
|
maxInput.value = "";
|
|
}
|
|
rowDiv.appendChild(maxInput);
|
|
|
|
// Attach the rowDiv to stepsList
|
|
stepsList.appendChild(rowDiv);
|
|
|
|
// 1) STEP CHANGE EVENT:
|
|
stepInput.addEventListener("change", () => {
|
|
const newStep = parseInt(stepInput.value, 10);
|
|
if (isNaN(newStep) || newStep < 1) {
|
|
// Revert if invalid
|
|
stepInput.value = this.node.properties.intSteps[i] || 1;
|
|
return;
|
|
}
|
|
// Update step
|
|
this.node.properties.intSteps[i] = newStep;
|
|
|
|
// Now auto-resnap existing Min to the new step
|
|
if (typeof this.node.properties.intMin[i] === "number") {
|
|
const oldMin = this.node.properties.intMin[i];
|
|
const steppedMin = newStep * Math.round(oldMin / newStep);
|
|
if (steppedMin !== oldMin) {
|
|
this.showEphemeralPopup(
|
|
`Min not aligned with step. Using stepped value: ${steppedMin}`,
|
|
stepInput.getBoundingClientRect().left,
|
|
stepInput.getBoundingClientRect().top - 25
|
|
);
|
|
}
|
|
this.node.properties.intMin[i] = steppedMin;
|
|
minInput.value = steppedMin.toString();
|
|
}
|
|
|
|
// Auto-resnap existing Max to the new step
|
|
if (typeof this.node.properties.intMax[i] === "number") {
|
|
const oldMax = this.node.properties.intMax[i];
|
|
const steppedMax = newStep * Math.round(oldMax / newStep);
|
|
if (steppedMax !== oldMax) {
|
|
this.showEphemeralPopup(
|
|
`Max not aligned with step. Using stepped value: ${steppedMax}`,
|
|
stepInput.getBoundingClientRect().left,
|
|
stepInput.getBoundingClientRect().top - 25
|
|
);
|
|
}
|
|
this.node.properties.intMax[i] = steppedMax;
|
|
maxInput.value = steppedMax.toString();
|
|
}
|
|
});
|
|
|
|
// 2) MIN CHANGE EVENT:
|
|
minInput.addEventListener("change", () => {
|
|
const val = parseInt(minInput.value, 10);
|
|
if (!isNaN(val)) {
|
|
const stepVal = this.node.properties.intSteps[i] || 1;
|
|
const steppedVal = stepVal * Math.round(val / stepVal);
|
|
if (steppedVal !== val) {
|
|
this.showEphemeralPopup(
|
|
`Min not aligned with step. Using stepped value: ${steppedVal}`,
|
|
minInput.getBoundingClientRect().left,
|
|
minInput.getBoundingClientRect().top - 25
|
|
);
|
|
}
|
|
this.node.properties.intMin[i] = steppedVal;
|
|
minInput.value = steppedVal.toString();
|
|
} else {
|
|
this.node.properties.intMin[i] = undefined;
|
|
minInput.value = "";
|
|
}
|
|
});
|
|
|
|
// 3) MAX CHANGE EVENT:
|
|
maxInput.addEventListener("change", () => {
|
|
const val = parseInt(maxInput.value, 10);
|
|
if (!isNaN(val)) {
|
|
const stepVal = this.node.properties.intSteps[i] || 1;
|
|
const steppedVal = stepVal * Math.round(val / stepVal);
|
|
if (steppedVal !== val) {
|
|
this.showEphemeralPopup(
|
|
`Max not aligned with step. Using stepped value: ${steppedVal}`,
|
|
maxInput.getBoundingClientRect().left,
|
|
maxInput.getBoundingClientRect().top - 25
|
|
);
|
|
}
|
|
this.node.properties.intMax[i] = steppedVal;
|
|
maxInput.value = steppedVal.toString();
|
|
} else {
|
|
this.node.properties.intMax[i] = undefined;
|
|
maxInput.value = "";
|
|
}
|
|
});
|
|
}
|
|
};
|
|
updateStepInputs();
|
|
|
|
const bgColorLabel = document.createElement("div");
|
|
bgColorLabel.textContent = "Node Background Color";
|
|
bgColorLabel.style.color = "#fff";
|
|
bgColorLabel.style.fontSize = "12px";
|
|
bgColorLabel.style.textAlign = "center";
|
|
content.appendChild(bgColorLabel);
|
|
|
|
const colorPicker = document.createElement("input");
|
|
colorPicker.type = "color";
|
|
colorPicker.value = this.node.properties.bgColor || "#181818";
|
|
colorPicker.style.marginTop = "-5px";
|
|
colorPicker.style.minHeight = "25px";
|
|
colorPicker.style.width = "100%";
|
|
colorPicker.style.marginBottom = "0px";
|
|
content.appendChild(colorPicker);
|
|
colorPicker.addEventListener("input", (e) => {
|
|
this.node.properties.bgColor = e.target.value;
|
|
this.node.setDirtyCanvas(true, true);
|
|
});
|
|
|
|
const rowBgColorLabel = document.createElement("div");
|
|
rowBgColorLabel.textContent = "Row Background Color";
|
|
rowBgColorLabel.style.color = "#fff";
|
|
rowBgColorLabel.style.fontSize = "12px";
|
|
rowBgColorLabel.style.textAlign = "center";
|
|
content.appendChild(rowBgColorLabel);
|
|
|
|
const rowBgColorPicker = document.createElement("input");
|
|
rowBgColorPicker.type = "color";
|
|
rowBgColorPicker.value = this.node.properties.rowBgColor || "#121212";
|
|
rowBgColorPicker.style.marginTop = "-5px";
|
|
rowBgColorPicker.style.minHeight = "25px";
|
|
rowBgColorPicker.style.width = "100%";
|
|
rowBgColorPicker.style.marginBottom = "0px";
|
|
content.appendChild(rowBgColorPicker);
|
|
rowBgColorPicker.addEventListener("input", (e) => {
|
|
this.node.properties.rowBgColor = e.target.value;
|
|
this.node.setDirtyCanvas(true, true);
|
|
});
|
|
|
|
const buttonColorLabel = document.createElement("div");
|
|
buttonColorLabel.textContent = "Int +/- Button Color";
|
|
buttonColorLabel.style.color = "#fff";
|
|
buttonColorLabel.style.fontSize = "12px";
|
|
buttonColorLabel.style.textAlign = "center";
|
|
content.appendChild(buttonColorLabel);
|
|
|
|
const buttonColorPicker = document.createElement("input");
|
|
buttonColorPicker.type = "color";
|
|
buttonColorPicker.value = this.node.properties.buttonColor || "#4b3e72";
|
|
buttonColorPicker.style.marginTop = "-5px";
|
|
buttonColorPicker.style.minHeight = "25px";
|
|
buttonColorPicker.style.width = "100%";
|
|
buttonColorPicker.style.marginBottom = "0px";
|
|
content.appendChild(buttonColorPicker);
|
|
buttonColorPicker.addEventListener("input", (e) => {
|
|
this.node.properties.buttonColor = e.target.value;
|
|
this.node.setDirtyCanvas(true, true);
|
|
});
|
|
|
|
const bottomPaddingLabel = document.createElement("div");
|
|
bottomPaddingLabel.textContent = "Bottom Padding (px)";
|
|
bottomPaddingLabel.style.color = "#fff";
|
|
bottomPaddingLabel.style.fontSize = "12px";
|
|
bottomPaddingLabel.style.textAlign = "center";
|
|
content.appendChild(bottomPaddingLabel);
|
|
|
|
const bottomPaddingInput = document.createElement("input");
|
|
bottomPaddingInput.type = "number";
|
|
bottomPaddingInput.value = this.node.properties.bottomPadding || 0;
|
|
bottomPaddingInput.style.width = "60px";
|
|
bottomPaddingInput.style.fontSize = "12px";
|
|
bottomPaddingInput.style.background = "#181818";
|
|
bottomPaddingInput.style.color = "#fff";
|
|
bottomPaddingInput.style.textAlign = "center";
|
|
content.appendChild(bottomPaddingInput);
|
|
bottomPaddingInput.addEventListener("change", () => {
|
|
const newVal = parseInt(bottomPaddingInput.value, 10);
|
|
if (!isNaN(newVal)) {
|
|
this.node.properties.bottomPadding = newVal;
|
|
this.setupOutputsAndWidgets();
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
});
|
|
|
|
minusBtn.addEventListener("click", (evt) => {
|
|
evt.stopPropagation();
|
|
const currentWidth = this.node.size[0];
|
|
if (this.node.properties.activeCount > 1) {
|
|
this.node.properties.activeCount = Math.max(1, this.node.properties.activeCount - 1);
|
|
this.adjustArrays();
|
|
updateStepInputs();
|
|
this.setupOutputsAndWidgets();
|
|
if (this.node.onOutputsChange) {
|
|
this.node.onOutputsChange();
|
|
}
|
|
this.node.setDirtyCanvas(true, true);
|
|
this.node.size[0] = currentWidth;
|
|
this.node.setDirtyCanvas(true, true);
|
|
setTimeout(() => popup.reposition(this.node.htmlElement, 10, 10), 50);
|
|
}
|
|
});
|
|
|
|
plusBtn.addEventListener("click", (evt) => {
|
|
evt.stopPropagation();
|
|
const currentWidth = this.node.size[0];
|
|
if (this.node.properties.activeCount < 20) {
|
|
this.node.properties.activeCount = Math.min(20, this.node.properties.activeCount + 1);
|
|
this.adjustArrays();
|
|
updateStepInputs();
|
|
this.setupOutputsAndWidgets();
|
|
if (this.node.onOutputsChange) {
|
|
this.node.onOutputsChange();
|
|
}
|
|
this.node.setDirtyCanvas(true, true);
|
|
this.node.size[0] = currentWidth;
|
|
this.node.setDirtyCanvas(true, true);
|
|
setTimeout(() => popup.reposition(this.node.htmlElement, 10, 10), 50);
|
|
}
|
|
});
|
|
}
|
|
|
|
createGearPopupFallback(x, y) {
|
|
const popup = new SettingsPopup(this.node);
|
|
popup.open(x, y);
|
|
this.populateGearPopup(popup);
|
|
}
|
|
|
|
showInlineIntEditor(index, rect, e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const graphCanvas = app.graphcanvas;
|
|
if (!graphCanvas) {
|
|
const fallbackX = e.clientX - (rect.w * 0.5);
|
|
const fallbackY = e.clientY - (rect.h * 0.5);
|
|
return this.spawnInlineEditorAtScreenPos(index, fallbackX, fallbackY, rect);
|
|
}
|
|
const nodeX = this.node.pos[0];
|
|
const nodeY = this.node.pos[1];
|
|
const localX = rect.x + (rect.w * 0.5);
|
|
const localY = rect.y + (rect.h * 0.5);
|
|
const scale = graphCanvas.ds.scale;
|
|
const offset = graphCanvas.ds.offset;
|
|
const canvasX = (nodeX + localX) * scale + offset[0];
|
|
const canvasY = (nodeY + localY) * scale + offset[1];
|
|
const canvasRect = graphCanvas.canvas.getBoundingClientRect();
|
|
const screenX = canvasRect.left + canvasX;
|
|
const screenY = canvasRect.top + canvasY;
|
|
this.spawnInlineEditorAtScreenPos(index, screenX, screenY, rect);
|
|
}
|
|
spawnInlineEditorAtScreenPos(index, screenX, screenY, rect) {
|
|
const input = document.createElement("input");
|
|
input.type = "text";
|
|
input.value = this.node.properties.values[index].toString();
|
|
input.style.position = "fixed";
|
|
const offsetX = -(rect.w * 0.5);
|
|
const offsetY = -(rect.h * 0.5);
|
|
const FixedOffsetX = 25;
|
|
const FixedOffsetY = 10;
|
|
input.style.left = (screenX + offsetX + FixedOffsetX) + "px";
|
|
input.style.top = (screenY + offsetY + FixedOffsetY) + "px";
|
|
input.style.width = Math.max(rect.w, 60) + "px";
|
|
input.style.height = Math.max(rect.h, 24) + "px";
|
|
input.style.fontSize = "14px";
|
|
input.style.zIndex = "10000";
|
|
input.style.background = "#181818";
|
|
input.style.color = "#fff";
|
|
input.style.border = "2px solid #4b3e72";
|
|
input.style.borderRadius = "6px";
|
|
input.style.outline = "none";
|
|
input.style.padding = "0 6px";
|
|
document.body.appendChild(input);
|
|
|
|
setTimeout(() => {
|
|
input.focus();
|
|
input.select();
|
|
}, 10);
|
|
|
|
const commit = () => {
|
|
let inputVal = input.value.trim();
|
|
if (inputVal.length > 0) {
|
|
let result;
|
|
try {
|
|
if (/^[0-9+\-*/().\s]+$/.test(inputVal)) {
|
|
result = eval(inputVal);
|
|
} else {
|
|
result = parseInt(inputVal, 10);
|
|
}
|
|
} catch {
|
|
result = parseInt(inputVal, 10);
|
|
}
|
|
|
|
if (!isNaN(result)) {
|
|
const messages = [];
|
|
const popupRect = input.getBoundingClientRect();
|
|
const popupX = popupRect.left;
|
|
const popupY = popupRect.top - 35;
|
|
|
|
const oldVal = result;
|
|
const step = this.node.properties.intSteps[index] || 1;
|
|
const steppedVal = step * Math.round(result / step);
|
|
|
|
// If step rounding changed the value
|
|
if (steppedVal !== oldVal) {
|
|
messages.push(`Set value not within step.`);
|
|
}
|
|
|
|
// Then clamp to min/max
|
|
const finalVal = this.clampValue(index, steppedVal);
|
|
|
|
// If clamp changed it again
|
|
if (finalVal !== steppedVal) {
|
|
const reason = finalVal === this.node.properties.intMin[index]
|
|
? `Min (${finalVal})`
|
|
: `Max (${finalVal})`;
|
|
messages.push(`Set value out of range.`);
|
|
}
|
|
|
|
// Now, if there were any adjustments, append a note of the final
|
|
if (messages.length > 0) {
|
|
messages.push(`Set value: ${finalVal}`);
|
|
this.showEphemeralPopup(messages.join(" | "), popupX, popupY);
|
|
}
|
|
|
|
this.node.properties.values[index] = finalVal;
|
|
if (this.node.widgets[index]) {
|
|
this.node.widgets[index].value = finalVal;
|
|
}
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
}
|
|
remove();
|
|
};
|
|
|
|
const remove = () => {
|
|
document.removeEventListener("click", outsideClick, true);
|
|
if (input.parentNode) {
|
|
input.parentNode.removeChild(input);
|
|
}
|
|
};
|
|
|
|
const outsideClick = (evt) => {
|
|
if (evt.target !== input) {
|
|
commit();
|
|
}
|
|
};
|
|
|
|
setTimeout(() => {
|
|
document.addEventListener("click", outsideClick, true);
|
|
}, 100);
|
|
|
|
input.addEventListener("keydown", (evt) => {
|
|
if (evt.key === "Enter") {
|
|
evt.preventDefault();
|
|
commit();
|
|
} else if (evt.key === "Escape") {
|
|
evt.preventDefault();
|
|
remove();
|
|
}
|
|
});
|
|
}
|
|
|
|
showEphemeralPopup(message, x, y) {
|
|
const popup = document.createElement("div");
|
|
popup.textContent = message;
|
|
popup.style.position = "fixed";
|
|
popup.style.left = x + "px";
|
|
popup.style.top = y + "px";
|
|
popup.style.padding = "6px 10px";
|
|
popup.style.fontSize = "12px";
|
|
popup.style.background = "#333";
|
|
popup.style.color = "#fff";
|
|
popup.style.border = "2px solid #4b3e72";
|
|
popup.style.borderRadius = "6px";
|
|
popup.style.zIndex = "999999";
|
|
document.body.appendChild(popup);
|
|
|
|
setTimeout(() => {
|
|
if (popup.parentNode) {
|
|
popup.parentNode.removeChild(popup);
|
|
}
|
|
}, 2000);
|
|
}
|
|
|
|
incrementValue(index) {
|
|
const step = (this.node.properties.intSteps && this.node.properties.intSteps[index]) || 1;
|
|
let v = this.node.properties.values[index] + step;
|
|
v = this.clampValue(index, v);
|
|
this.node.properties.values[index] = v;
|
|
if (this.node.widgets[index]) {
|
|
this.node.widgets[index].value = v;
|
|
}
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
|
|
decrementValue(index) {
|
|
const step = (this.node.properties.intSteps && this.node.properties.intSteps[index]) || 1;
|
|
let v = this.node.properties.values[index] - step;
|
|
v = this.clampValue(index, v);
|
|
this.node.properties.values[index] = v;
|
|
if (this.node.widgets[index]) {
|
|
this.node.widgets[index].value = v;
|
|
}
|
|
this.node.setDirtyCanvas(true, true);
|
|
}
|
|
|
|
adjustArrays() {
|
|
const count = this.node.properties.activeCount;
|
|
while (this.node.properties.labels.length < count) {
|
|
this.node.properties.labels.push(`i${this.node.properties.labels.length + 1}`);
|
|
}
|
|
this.node.properties.labels.length = count;
|
|
while (this.node.properties.values.length < count) {
|
|
this.node.properties.values.push(0);
|
|
}
|
|
this.node.properties.values.length = count;
|
|
while (this.node.properties.intSteps.length < count) {
|
|
this.node.properties.intSteps.push(1);
|
|
}
|
|
this.node.properties.intSteps.length = count;
|
|
while (this.node.properties.intMin.length < count) {
|
|
this.node.properties.intMin.push(undefined);
|
|
}
|
|
this.node.properties.intMin.length = count;
|
|
while (this.node.properties.intMax.length < count) {
|
|
this.node.properties.intMax.push(undefined);
|
|
}
|
|
this.node.properties.intMax.length = count;
|
|
}
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "AIDocsClinicalTools.MultiInt",
|
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
|
if (nodeData.name === "MultiInt") {
|
|
Object.assign(nodeType.prototype, {
|
|
onNodeCreated() {
|
|
this.multiIntNode = new MultiIntNode(this);
|
|
},
|
|
onMouseDown(e, pos) {
|
|
return this.multiIntNode.onMouseDown(e, pos);
|
|
},
|
|
});
|
|
}
|
|
}
|
|
});
|