Spaces:
Running
on
L40S
Running
on
L40S
File size: 7,962 Bytes
4450790 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
import { app } from "scripts/app.js";
import { RgthreeBaseVirtualNodeConstructor } from "typings/rgthree.js";
import { RgthreeBaseVirtualNode } from "./base_node.js";
import { NodeTypesString } from "./constants.js";
import type {
LGraphCanvas as TLGraphCanvas,
LGraphNode,
AdjustedMouseEvent,
Vector2,
} from "typings/litegraph.js";
import { rgthree } from "./rgthree.js";
/**
* A label node that allows you to put floating text anywhere on the graph. The text is the `Title`
* and the font size, family, color, alignment as well as a background color, padding, and
* background border radius can all be adjusted in the properties. Multiline text can be added from
* the properties panel (because ComfyUI let's you shift + enter there, only).
*/
export class Label extends RgthreeBaseVirtualNode {
static override type = NodeTypesString.LABEL;
static override title = NodeTypesString.LABEL;
override comfyClass = NodeTypesString.LABEL;
static readonly title_mode = LiteGraph.NO_TITLE;
static collapsable = false;
static "@fontSize" = { type: "number" };
static "@fontFamily" = { type: "string" };
static "@fontColor" = { type: "string" };
static "@textAlign" = { type: "combo", values: ["left", "center", "right"] };
static "@backgroundColor" = { type: "string" };
static "@padding" = { type: "number" };
static "@borderRadius" = { type: "number" };
override resizable = false;
constructor(title = Label.title) {
super(title);
this.properties["fontSize"] = 12;
this.properties["fontFamily"] = "Arial";
this.properties["fontColor"] = "#ffffff";
this.properties["textAlign"] = "left";
this.properties["backgroundColor"] = "transparent";
this.properties["padding"] = 0;
this.properties["borderRadius"] = 0;
this.color = "#fff0";
this.bgcolor = "#fff0";
this.onConstructed();
}
draw(ctx: CanvasRenderingContext2D) {
this.flags = this.flags || {};
this.flags.allow_interaction = !this.flags.pinned;
ctx.save();
this.color = "#fff0";
this.bgcolor = "#fff0";
const fontColor = this.properties["fontColor"] || "#ffffff";
const backgroundColor = this.properties["backgroundColor"] || "";
ctx.font = `${Math.max(this.properties["fontSize"] || 0, 1)}px ${
this.properties["fontFamily"] ?? "Arial"
}`;
const padding = Number(this.properties["padding"]) ?? 0;
const lines = this.title.replace(/\n*$/, "").split("\n");
const maxWidth = Math.max(...lines.map((s) => ctx.measureText(s).width));
this.size[0] = maxWidth + padding * 2;
this.size[1] = this.properties["fontSize"] * lines.length + padding * 2;
if (backgroundColor) {
ctx.beginPath();
const borderRadius = Number(this.properties["borderRadius"]) || 0;
ctx.roundRect(0, 0, this.size[0], this.size[1], [borderRadius]);
ctx.fillStyle = backgroundColor;
ctx.fill();
}
ctx.textAlign = "left";
let textX = padding;
if (this.properties["textAlign"] === "center") {
ctx.textAlign = "center";
textX = this.size[0] / 2;
} else if (this.properties["textAlign"] === "right") {
ctx.textAlign = "right";
textX = this.size[0] - padding;
}
ctx.textBaseline = "top";
ctx.fillStyle = fontColor;
let currentY = padding;
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i] || " ", textX, currentY);
currentY += this.properties["fontSize"];
}
ctx.restore();
}
override onDblClick(event: AdjustedMouseEvent, pos: Vector2, canvas: TLGraphCanvas) {
// Since everything we can do here is in the properties, let's pop open the properties panel.
LGraphCanvas.active_canvas.showShowNodePanel(this);
}
override onShowCustomPanelInfo(panel: HTMLElement) {
panel.querySelector('div.property[data-property="Mode"]')?.remove();
panel.querySelector('div.property[data-property="Color"]')?.remove();
}
override inResizeCorner(x: number, y: number) {
// A little ridiculous there's both a resizable property and this method separately to draw the
// resize icon...
return this.resizable;
}
override getHelp() {
return `
<p>
The rgthree-comfy ${this.type!.replace("(rgthree)", "")} node allows you to add a floating
label to your workflow.
</p>
<p>
The text shown is the "Title" of the node and you can adjust the the font size, font family,
font color, text alignment as well as a background color, padding, and background border
radius from the node's properties. You can double-click the node to open the properties
panel.
<p>
<ul>
<li>
<p>
<strong>Pro tip #1:</strong> You can add multiline text from the properties panel
<i>(because ComfyUI let's you shift + enter there, only)</i>.
</p>
</li>
<li>
<p>
<strong>Pro tip #2:</strong> You can use ComfyUI's native "pin" option in the
right-click menu to make the label stick to the workflow and clicks to "go through".
You can right-click at any time to unpin.
</p>
</li>
<li>
<p>
<strong>Pro tip #3:</strong> Color values are hexidecimal strings, like "#FFFFFF" for
white, or "#660000" for dark red. You can supply a 7th & 8th value (or 5th if using
shorthand) to create a transluscent color. For instance, "#FFFFFF88" is semi-transparent
white.
</p>
</li>
</ul>`;
}
}
/**
* We override the drawNode to see if we're drawing our label and, if so, hijack it so we can draw
* it like we want. We also do call out to oldDrawNode, which takes care of very minimal things,
* like a select box.
*/
const oldDrawNode = LGraphCanvas.prototype.drawNode;
LGraphCanvas.prototype.drawNode = function (node: LGraphNode, ctx: CanvasRenderingContext2D) {
if (node.constructor === Label) {
// These get set very aggressively; maybe an extension is doing it. We'll just clear them out
// each time.
(node as Label).bgcolor = "transparent";
(node as Label).color = "transparent";
const v = oldDrawNode.apply(this, arguments as any);
(node as Label).draw(ctx);
return v;
}
const v = oldDrawNode.apply(this, arguments as any);
return v;
};
/**
* We override LGraph getNodeOnPos to see if we're being called while also processing a mouse down
* and, if so, filter out any label nodes on labels that are pinned. This makes the click go
* "through" the label. We still allow right clicking (so you can unpin) and double click for the
* properties panel, though that takes two double clicks (one to select, one to actually double
* click).
*/
const oldGetNodeOnPos = LGraph.prototype.getNodeOnPos;
LGraph.prototype.getNodeOnPos = function <T extends LGraphNode>(
x: number,
y: number,
nodes_list?: LGraphNode[],
margin?: number,
) {
if (
// processMouseDown always passes in the nodes_list
nodes_list &&
rgthree.processingMouseDown &&
rgthree.lastAdjustedMouseEvent?.type.includes("down") &&
rgthree.lastAdjustedMouseEvent?.which === 1
) {
// Using the same logic from LGraphCanvas processMouseDown, let's see if we consider this a
// double click.
let isDoubleClick = LiteGraph.getTime() - LGraphCanvas.active_canvas.last_mouseclick < 300;
if (!isDoubleClick) {
nodes_list = [...nodes_list].filter((n) => !(n instanceof Label) || !n.flags?.pinned);
}
}
return oldGetNodeOnPos.apply(this, [x, y, nodes_list, margin]) as T | null;
};
// Register the extension.
app.registerExtension({
name: "rgthree.Label",
registerCustomNodes() {
Label.setUp();
},
});
|