[Refactor] Split node slot code out to base class (#994)

Foundational work for subgraph.
This commit is contained in:
filtered
2025-05-02 08:26:25 +10:00
committed by GitHub
parent 2e0267105e
commit 388a3d64cc
6 changed files with 56 additions and 37 deletions

View File

@@ -3521,7 +3521,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
isValidTarget || isValidTarget ||
!slot.isWidgetInputSlot || !slot.isWidgetInputSlot ||
this.#isMouseOverWidget(this.getWidgetFromSlot(slot)) || this.#isMouseOverWidget(this.getWidgetFromSlot(slot)) ||
slot.isConnected() slot.isConnected
) { ) {
ctx.globalAlpha = isValid ? editorAlpha : 0.4 * editorAlpha ctx.globalAlpha = isValid ? editorAlpha : 0.4 * editorAlpha
slot.draw(ctx, { slot.draw(ctx, {

View File

@@ -38,7 +38,7 @@ export interface HasBoundingRect {
* *
* Used for various calculations, such as overlap, selective rendering, and click checks. * Used for various calculations, such as overlap, selective rendering, and click checks.
* For most items, this is cached position & size as `x, y, width, height`. * For most items, this is cached position & size as `x, y, width, height`.
* Some items (such as nodes) may extend above and/or to the left of their {@link pos}. * Some items (such as nodes and slots) may extend above and/or to the left of their {@link pos}.
* @readonly * @readonly
* @see {@link move} * @see {@link move}
*/ */

View File

@@ -22,7 +22,7 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
this.link = slot.link this.link = slot.link
} }
override isConnected(): boolean { override get isConnected(): boolean {
return this.link != null return this.link != null
} }
@@ -31,7 +31,7 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
} }
override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) { override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) {
const originalTextAlign = ctx.textAlign const { textAlign } = ctx
ctx.textAlign = "left" ctx.textAlign = "left"
super.draw(ctx, { super.draw(ctx, {
@@ -40,6 +40,6 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
doStroke: false, doStroke: false,
}) })
ctx.textAlign = originalTextAlign ctx.textAlign = textAlign
} }
} }

View File

@@ -36,13 +36,12 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
return "link" in fromSlot && LiteGraph.isValidConnection(this.type, fromSlot.type) return "link" in fromSlot && LiteGraph.isValidConnection(this.type, fromSlot.type)
} }
override isConnected(): boolean { override get isConnected(): boolean {
return this.links != null && this.links.length > 0 return this.links != null && this.links.length > 0
} }
override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) { override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) {
const originalTextAlign = ctx.textAlign const { textAlign, strokeStyle } = ctx
const originalStrokeStyle = ctx.strokeStyle
ctx.textAlign = "right" ctx.textAlign = "right"
ctx.strokeStyle = "black" ctx.strokeStyle = "black"
@@ -52,7 +51,7 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
doStroke: true, doStroke: true,
}) })
ctx.textAlign = originalTextAlign ctx.textAlign = textAlign
ctx.strokeStyle = originalStrokeStyle ctx.strokeStyle = strokeStyle
} }
} }

View File

@@ -1,4 +1,4 @@
import type { CanvasColour, DefaultConnectionColors, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetLocator, OptionalProps, Point, ReadOnlyPoint, Rect } from "@/interfaces" import type { CanvasColour, DefaultConnectionColors, INodeInputSlot, INodeOutputSlot, INodeSlot, OptionalProps, Point, ReadOnlyPoint } from "@/interfaces"
import type { LGraphNode } from "@/LGraphNode" import type { LGraphNode } from "@/LGraphNode"
import { LabelPosition, SlotShape, SlotType } from "@/draw" import { LabelPosition, SlotShape, SlotType } from "@/draw"
@@ -7,6 +7,7 @@ import { getCentre } from "@/measure"
import { LinkDirection, RenderShape } from "@/types/globalEnums" import { LinkDirection, RenderShape } from "@/types/globalEnums"
import { NodeInputSlot } from "./NodeInputSlot" import { NodeInputSlot } from "./NodeInputSlot"
import { SlotBase } from "./SlotBase"
export interface IDrawOptions { export interface IDrawOptions {
colorContext: DefaultConnectionColors colorContext: DefaultConnectionColors
@@ -16,22 +17,9 @@ export interface IDrawOptions {
highlight?: boolean highlight?: boolean
} }
export abstract class NodeSlot implements INodeSlot { /** Shared base class for {@link LGraphNode} input and output slots. */
name: string export abstract class NodeSlot extends SlotBase implements INodeSlot {
localized_name?: string
label?: string
type: ISlotType
dir?: LinkDirection
removable?: boolean
shape?: RenderShape
color_off?: CanvasColour
color_on?: CanvasColour
locked?: boolean
nameLocked?: boolean
pos?: Point pos?: Point
widget?: IWidgetLocator
hasErrors?: boolean
readonly boundingRect: Rect
/** The offset from the parent node to the centre point of this slot. */ /** The offset from the parent node to the centre point of this slot. */
get #centreOffset(): ReadOnlyPoint { get #centreOffset(): ReadOnlyPoint {
@@ -64,10 +52,9 @@ export abstract class NodeSlot implements INodeSlot {
abstract get isWidgetInputSlot(): boolean abstract get isWidgetInputSlot(): boolean
constructor(slot: OptionalProps<INodeSlot, "boundingRect">, node: LGraphNode) { constructor(slot: OptionalProps<INodeSlot, "boundingRect">, node: LGraphNode) {
super(slot.name, slot.type, slot.boundingRect ?? [0, 0, 0, 0])
Object.assign(this, slot) Object.assign(this, slot)
this.name = slot.name
this.type = slot.type
this.boundingRect = slot.boundingRect ?? [0, 0, 0, 0]
this.#node = node this.#node = node
} }
@@ -84,14 +71,6 @@ export abstract class NodeSlot implements INodeSlot {
return this.label || this.localized_name || this.name || "" return this.label || this.localized_name || this.name || ""
} }
abstract isConnected(): boolean
renderingColor(colorContext: DefaultConnectionColors): CanvasColour {
return this.isConnected()
? this.color_on || colorContext.getConnectedColor(this.type)
: this.color_off || colorContext.getDisconnectedColor(this.type)
}
draw( draw(
ctx: CanvasRenderingContext2D, ctx: CanvasRenderingContext2D,
{ {

41
src/node/SlotBase.ts Normal file
View File

@@ -0,0 +1,41 @@
import type { CanvasColour, DefaultConnectionColors, INodeSlot, ISlotType, IWidgetLocator, Point, Rect } from "@/interfaces"
import type { LLink } from "@/LLink"
import type { RenderShape } from "@/types/globalEnums"
import type { LinkDirection } from "@/types/globalEnums"
/** Base class for all input & output slots. */
export abstract class SlotBase implements INodeSlot {
name: string
localized_name?: string
label?: string
type: ISlotType
dir?: LinkDirection
removable?: boolean
shape?: RenderShape
color_off?: CanvasColour
color_on?: CanvasColour
locked?: boolean
nameLocked?: boolean
widget?: IWidgetLocator
_floatingLinks?: Set<LLink>
hasErrors?: boolean
/** The centre point of the slot. */
abstract pos?: Point
readonly boundingRect: Rect
constructor(name: string, type: ISlotType, boundingRect: Rect) {
this.name = name
this.type = type
this.boundingRect = boundingRect
}
abstract get isConnected(): boolean
renderingColor(colorContext: DefaultConnectionColors): CanvasColour {
return this.isConnected
? this.color_on || colorContext.getConnectedColor(this.type)
: this.color_off || colorContext.getDisconnectedColor(this.type)
}
}