mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-02 06:19:58 +00:00
120 lines
3.9 KiB
TypeScript
120 lines
3.9 KiB
TypeScript
import type { SubgraphOutput } from "./SubgraphOutput"
|
|
import type { LinkConnector } from "@/canvas/LinkConnector"
|
|
import type { CanvasPointer } from "@/CanvasPointer"
|
|
import type { DefaultConnectionColors, ISlotType, Positionable } from "@/interfaces"
|
|
import type { INodeOutputSlot } from "@/interfaces"
|
|
import type { LGraphNode, NodeId } from "@/LGraphNode"
|
|
import type { LLink } from "@/LLink"
|
|
import type { RerouteId } from "@/Reroute"
|
|
import type { CanvasPointerEvent } from "@/types/events"
|
|
import type { NodeLike } from "@/types/NodeLike"
|
|
import type { SubgraphIO } from "@/types/serialisation"
|
|
|
|
import { SUBGRAPH_OUTPUT_ID } from "@/constants"
|
|
import { Rectangle } from "@/infrastructure/Rectangle"
|
|
import { findFreeSlotOfType } from "@/utils/collections"
|
|
|
|
import { EmptySubgraphOutput } from "./EmptySubgraphOutput"
|
|
import { SubgraphIONodeBase } from "./SubgraphIONodeBase"
|
|
|
|
export class SubgraphOutputNode extends SubgraphIONodeBase<SubgraphOutput> implements Positionable {
|
|
readonly id: NodeId = SUBGRAPH_OUTPUT_ID
|
|
|
|
readonly emptySlot: EmptySubgraphOutput = new EmptySubgraphOutput(this)
|
|
|
|
get slots() {
|
|
return this.subgraph.outputs
|
|
}
|
|
|
|
override get allSlots(): SubgraphOutput[] {
|
|
return [...this.slots, this.emptySlot]
|
|
}
|
|
|
|
get slotAnchorX() {
|
|
const [x] = this.boundingRect
|
|
return x + SubgraphIONodeBase.roundedRadius
|
|
}
|
|
|
|
override onPointerDown(e: CanvasPointerEvent, pointer: CanvasPointer, linkConnector: LinkConnector): void {
|
|
// Left-click handling for dragging connections
|
|
if (e.button === 0) {
|
|
for (const slot of this.allSlots) {
|
|
const slotBounds = Rectangle.fromCentre(slot.pos, slot.boundingRect.height)
|
|
|
|
if (slotBounds.containsXy(e.canvasX, e.canvasY)) {
|
|
pointer.onDragStart = () => {
|
|
linkConnector.dragNewFromSubgraphOutput(this.subgraph, this, slot)
|
|
}
|
|
pointer.onDragEnd = (eUp) => {
|
|
linkConnector.dropLinks(this.subgraph, eUp)
|
|
}
|
|
pointer.finally = () => {
|
|
linkConnector.reset(true)
|
|
}
|
|
}
|
|
}
|
|
// Check for right-click
|
|
} else if (e.button === 2) {
|
|
const slot = this.getSlotInPosition(e.canvasX, e.canvasY)
|
|
if (slot) this.showSlotContextMenu(slot, e)
|
|
}
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
override renameSlot(slot: SubgraphOutput, name: string): void {
|
|
this.subgraph.renameOutput(slot, name)
|
|
}
|
|
|
|
/** @inheritdoc */
|
|
override removeSlot(slot: SubgraphOutput): void {
|
|
this.subgraph.removeOutput(slot)
|
|
}
|
|
|
|
canConnectTo(outputNode: NodeLike, fromSlot: SubgraphOutput, output: INodeOutputSlot | SubgraphIO): boolean {
|
|
return outputNode.canConnectTo(this, fromSlot, output)
|
|
}
|
|
|
|
connectByTypeOutput(
|
|
slot: number,
|
|
target_node: LGraphNode,
|
|
target_slotType: ISlotType,
|
|
optsIn?: { afterRerouteId?: RerouteId },
|
|
): LLink | undefined {
|
|
const outputSlot = target_node.findOutputByType(target_slotType)
|
|
if (!outputSlot) return
|
|
|
|
return this.slots[slot].connect(outputSlot.slot, target_node, optsIn?.afterRerouteId)
|
|
}
|
|
|
|
findInputByType(type: ISlotType): SubgraphOutput | undefined {
|
|
return findFreeSlotOfType(this.slots, type, slot => slot.linkIds.length > 0)?.slot
|
|
}
|
|
|
|
override drawProtected(ctx: CanvasRenderingContext2D, colorContext: DefaultConnectionColors): void {
|
|
const { roundedRadius } = SubgraphIONodeBase
|
|
const transform = ctx.getTransform()
|
|
|
|
const [x, y, , height] = this.boundingRect
|
|
ctx.translate(x, y)
|
|
|
|
// Draw bottom rounded part
|
|
ctx.strokeStyle = this.sideStrokeStyle
|
|
ctx.lineWidth = this.sideLineWidth
|
|
ctx.beginPath()
|
|
ctx.arc(roundedRadius, roundedRadius, roundedRadius, Math.PI, Math.PI * 1.5)
|
|
|
|
// Straight line to bottom
|
|
ctx.moveTo(0, roundedRadius)
|
|
ctx.lineTo(0, height - roundedRadius)
|
|
|
|
// Bottom rounded part
|
|
ctx.arc(roundedRadius, height - roundedRadius, roundedRadius, Math.PI, Math.PI * 0.5, true)
|
|
ctx.stroke()
|
|
|
|
// Restore context
|
|
ctx.setTransform(transform)
|
|
|
|
this.drawSlots(ctx, colorContext)
|
|
}
|
|
}
|