mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 14:27:40 +00:00
Add litegraph input indicator helper class (#842)
Example usage with ComfyUI_frontend, via console / devtools: ```ts const inputIndicators = new InputIndicators(app.canvas) // Dispose: inputIndicators.dispose() ```
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Dictionary, ISlotType, Rect, WhenNullish } from "./interfaces"
|
||||
|
||||
import { InputIndicators } from "./canvas/InputIndicators"
|
||||
import { ContextMenu } from "./ContextMenu"
|
||||
import { CurveEditor } from "./CurveEditor"
|
||||
import { DragAndScale } from "./DragAndScale"
|
||||
@@ -270,6 +271,7 @@ export class LiteGraphGlobal {
|
||||
ContextMenu = ContextMenu
|
||||
CurveEditor = CurveEditor
|
||||
Reroute = Reroute
|
||||
InputIndicators = InputIndicators
|
||||
|
||||
onNodeTypeRegistered?(type: string, base_class: typeof LGraphNode): void
|
||||
onNodeTypeReplaced?(type: string, base_class: typeof LGraphNode, prev: unknown): void
|
||||
|
||||
167
src/canvas/InputIndicators.ts
Normal file
167
src/canvas/InputIndicators.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import type { LGraphCanvas } from "@/LGraphCanvas"
|
||||
|
||||
/**
|
||||
* A class that can be added to the render cycle to show pointer / keyboard status symbols.
|
||||
*
|
||||
* Used to create videos of feature changes.
|
||||
*
|
||||
* Example usage with ComfyUI_frontend, via console / devtools:
|
||||
*
|
||||
* ```ts
|
||||
* const inputIndicators = new InputIndicators(canvas)
|
||||
* // Dispose:
|
||||
* inputIndicators.dispose()
|
||||
* ```
|
||||
*/
|
||||
export class InputIndicators {
|
||||
// #region config
|
||||
radius = 8
|
||||
startAngle = 0
|
||||
endAngle = Math.PI * 2
|
||||
|
||||
inactiveColour = "#ffffff10"
|
||||
colour1 = "#ff5f00"
|
||||
colour2 = "#00ff7c"
|
||||
colour3 = "#dea7ff"
|
||||
fontString = "bold 12px Arial"
|
||||
// #endregion
|
||||
|
||||
// #region state
|
||||
enabled: boolean = true
|
||||
|
||||
shiftDown: boolean = false
|
||||
undoDown: boolean = false
|
||||
redoDown: boolean = false
|
||||
ctrlDown: boolean = false
|
||||
altDown: boolean = false
|
||||
mouse0Down: boolean = false
|
||||
mouse1Down: boolean = false
|
||||
mouse2Down: boolean = false
|
||||
|
||||
x: number = 0
|
||||
y: number = 0
|
||||
// #endregion
|
||||
|
||||
controller?: AbortController
|
||||
|
||||
constructor(public canvas: LGraphCanvas) {
|
||||
this.controller = new AbortController()
|
||||
const { signal } = this.controller
|
||||
|
||||
const element = canvas.canvas
|
||||
const options = { capture: true, signal } satisfies AddEventListenerOptions
|
||||
|
||||
element.addEventListener("pointerdown", this.#onPointerDownOrMove, options)
|
||||
element.addEventListener("pointermove", this.#onPointerDownOrMove, options)
|
||||
element.addEventListener("pointerup", this.#onPointerUp, options)
|
||||
element.addEventListener("keydown", this.#onKeyDownOrUp, options)
|
||||
document.addEventListener("keyup", this.#onKeyDownOrUp, options)
|
||||
|
||||
const origDrawFrontCanvas = canvas.drawFrontCanvas.bind(canvas)
|
||||
signal.addEventListener("abort", () => {
|
||||
canvas.drawFrontCanvas = origDrawFrontCanvas
|
||||
})
|
||||
|
||||
canvas.drawFrontCanvas = () => {
|
||||
origDrawFrontCanvas()
|
||||
this.draw()
|
||||
}
|
||||
}
|
||||
|
||||
#onPointerDownOrMove = this.onPointerDownOrMove.bind(this)
|
||||
onPointerDownOrMove(e: MouseEvent): void {
|
||||
this.mouse0Down = (e.buttons & 1) === 1
|
||||
this.mouse1Down = (e.buttons & 4) === 4
|
||||
this.mouse2Down = (e.buttons & 2) === 2
|
||||
|
||||
this.x = e.clientX
|
||||
this.y = e.clientY
|
||||
|
||||
this.canvas.setDirty(true)
|
||||
}
|
||||
|
||||
#onPointerUp = this.onPointerUp.bind(this)
|
||||
onPointerUp(): void {
|
||||
this.mouse0Down = false
|
||||
this.mouse1Down = false
|
||||
this.mouse2Down = false
|
||||
}
|
||||
|
||||
#onKeyDownOrUp = this.onKeyDownOrUp.bind(this)
|
||||
onKeyDownOrUp(e: KeyboardEvent): void {
|
||||
this.ctrlDown = e.ctrlKey
|
||||
this.altDown = e.altKey
|
||||
this.shiftDown = e.shiftKey
|
||||
this.undoDown = e.ctrlKey && e.code === "KeyZ" && e.type === "keydown"
|
||||
this.redoDown = e.ctrlKey && e.code === "KeyY" && e.type === "keydown"
|
||||
}
|
||||
|
||||
draw() {
|
||||
const {
|
||||
canvas: { ctx },
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
x,
|
||||
y,
|
||||
inactiveColour,
|
||||
colour1,
|
||||
colour2,
|
||||
colour3,
|
||||
fontString,
|
||||
} = this
|
||||
|
||||
const { fillStyle, font } = ctx
|
||||
|
||||
const mouseDotX = x
|
||||
const mouseDotY = y - 80
|
||||
|
||||
const textX = mouseDotX
|
||||
const textY = mouseDotY - 15
|
||||
ctx.font = fontString
|
||||
|
||||
textMarker(textX + 0, textY, "Shift", this.shiftDown ? colour1 : inactiveColour)
|
||||
textMarker(textX + 45, textY + 20, "Alt", this.altDown ? colour2 : inactiveColour)
|
||||
textMarker(textX + 30, textY, "Control", this.ctrlDown ? colour3 : inactiveColour)
|
||||
textMarker(textX - 30, textY, "↩️", this.undoDown ? "#000" : "transparent")
|
||||
textMarker(textX + 45, textY, "↪️", this.redoDown ? "#000" : "transparent")
|
||||
|
||||
ctx.beginPath()
|
||||
drawDot(mouseDotX, mouseDotY)
|
||||
drawDot(mouseDotX + 15, mouseDotY)
|
||||
drawDot(mouseDotX + 30, mouseDotY)
|
||||
ctx.fillStyle = inactiveColour
|
||||
ctx.fill()
|
||||
|
||||
const leftButtonColour = this.mouse0Down ? colour1 : inactiveColour
|
||||
const middleButtonColour = this.mouse1Down ? colour2 : inactiveColour
|
||||
const rightButtonColour = this.mouse2Down ? colour3 : inactiveColour
|
||||
if (this.mouse0Down) mouseMarker(mouseDotX, mouseDotY, leftButtonColour)
|
||||
if (this.mouse1Down) mouseMarker(mouseDotX + 15, mouseDotY, middleButtonColour)
|
||||
if (this.mouse2Down) mouseMarker(mouseDotX + 30, mouseDotY, rightButtonColour)
|
||||
|
||||
ctx.fillStyle = fillStyle
|
||||
ctx.font = font
|
||||
|
||||
function textMarker(x: number, y: number, text: string, colour: string) {
|
||||
ctx.fillStyle = colour
|
||||
ctx.fillText(text, x, y)
|
||||
}
|
||||
|
||||
function mouseMarker(x: number, y: number, colour: string) {
|
||||
ctx.beginPath()
|
||||
ctx.fillStyle = colour
|
||||
drawDot(x, y)
|
||||
ctx.fill()
|
||||
}
|
||||
|
||||
function drawDot(x: number, y: number) {
|
||||
ctx.arc(x, y, radius, startAngle, endAngle)
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.controller?.abort()
|
||||
this.controller = undefined
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,7 @@ export interface LGraphNodeConstructor<T extends LGraphNode = LGraphNode> {
|
||||
|
||||
// End backwards compat
|
||||
|
||||
export { InputIndicators } from "./canvas/InputIndicators"
|
||||
export { isOverNodeInput, isOverNodeOutput } from "./canvas/measureSlots"
|
||||
export { CanvasPointer } from "./CanvasPointer"
|
||||
export { ContextMenu } from "./ContextMenu"
|
||||
|
||||
@@ -29,6 +29,7 @@ LiteGraphGlobal {
|
||||
"Globals": {},
|
||||
"HIDDEN_LINK": -1,
|
||||
"INPUT": 1,
|
||||
"InputIndicators": [Function],
|
||||
"LEFT": 3,
|
||||
"LGraph": [Function],
|
||||
"LGraphCanvas": [Function],
|
||||
|
||||
Reference in New Issue
Block a user