mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 19:21:54 +00:00
Reduce input socket hitbox for widgets (#966)
Restores the full left-arrow button click area for widgets. Previously lost ~5 canvas pixels to clicks intercepted by input sockets. Supporting refactors: - Maps concrete node slot impls. to private array, once per frame - Converts slot boundingRect to use absolute canvas pos (same as other elements) - Stores parent node ref in concrete slot classes
This commit is contained in:
@@ -58,6 +58,7 @@ import {
|
|||||||
overlapBounding,
|
overlapBounding,
|
||||||
snapPoint,
|
snapPoint,
|
||||||
} from "./measure"
|
} from "./measure"
|
||||||
|
import { NodeInputSlot } from "./node/NodeInputSlot"
|
||||||
import { Reroute, type RerouteId } from "./Reroute"
|
import { Reroute, type RerouteId } from "./Reroute"
|
||||||
import { stringOrEmpty } from "./strings"
|
import { stringOrEmpty } from "./strings"
|
||||||
import {
|
import {
|
||||||
@@ -2277,7 +2278,11 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
if (inputs) {
|
if (inputs) {
|
||||||
for (const [i, input] of inputs.entries()) {
|
for (const [i, input] of inputs.entries()) {
|
||||||
const link_pos = node.getInputPos(i)
|
const link_pos = node.getInputPos(i)
|
||||||
if (isInRectangle(x, y, link_pos[0] - 15, link_pos[1] - 10, 30, 20)) {
|
const isInSlot = input instanceof NodeInputSlot
|
||||||
|
? isInRect(x, y, input.boundingRect)
|
||||||
|
: isInRectangle(x, y, link_pos[0] - 15, link_pos[1] - 10, 30, 20)
|
||||||
|
|
||||||
|
if (isInSlot) {
|
||||||
pointer.onDoubleClick = () => node.onInputDblClick?.(i, e)
|
pointer.onDoubleClick = () => node.onInputDblClick?.(i, e)
|
||||||
pointer.onClick = () => node.onInputClick?.(i, e)
|
pointer.onClick = () => node.onInputClick?.(i, e)
|
||||||
|
|
||||||
@@ -4312,6 +4317,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
ctx.font = this.inner_text_font
|
ctx.font = this.inner_text_font
|
||||||
|
|
||||||
// render inputs and outputs
|
// render inputs and outputs
|
||||||
|
node._setConcreteSlots()
|
||||||
if (!node.collapsed) {
|
if (!node.collapsed) {
|
||||||
node.arrange()
|
node.arrange()
|
||||||
node.drawSlots(ctx, {
|
node.drawSlots(ctx, {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import { LLink } from "./LLink"
|
|||||||
import { createBounds, isInRect, isInRectangle, isPointInRect, snapPoint } from "./measure"
|
import { createBounds, isInRect, isInRectangle, isPointInRect, snapPoint } from "./measure"
|
||||||
import { NodeInputSlot } from "./node/NodeInputSlot"
|
import { NodeInputSlot } from "./node/NodeInputSlot"
|
||||||
import { NodeOutputSlot } from "./node/NodeOutputSlot"
|
import { NodeOutputSlot } from "./node/NodeOutputSlot"
|
||||||
import { inputAsSerialisable, isINodeInputSlot, isWidgetInputSlot, outputAsSerialisable, toNodeSlotClass } from "./node/slotUtils"
|
import { inputAsSerialisable, isINodeInputSlot, isWidgetInputSlot, outputAsSerialisable } from "./node/slotUtils"
|
||||||
import {
|
import {
|
||||||
LGraphEventMode,
|
LGraphEventMode,
|
||||||
NodeSlotType,
|
NodeSlotType,
|
||||||
@@ -210,6 +210,10 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
type: string = ""
|
type: string = ""
|
||||||
inputs: INodeInputSlot[] = []
|
inputs: INodeInputSlot[] = []
|
||||||
outputs: INodeOutputSlot[] = []
|
outputs: INodeOutputSlot[] = []
|
||||||
|
|
||||||
|
#concreteInputs: NodeInputSlot[] = []
|
||||||
|
#concreteOutputs: NodeOutputSlot[] = []
|
||||||
|
|
||||||
// Not used
|
// Not used
|
||||||
connections: unknown[] = []
|
connections: unknown[] = []
|
||||||
properties: Dictionary<NodeProperty | undefined> = {}
|
properties: Dictionary<NodeProperty | undefined> = {}
|
||||||
@@ -697,7 +701,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.inputs ??= []
|
this.inputs ??= []
|
||||||
this.inputs = this.inputs.map(input => toClass(NodeInputSlot, input))
|
this.inputs = this.inputs.map(input => toClass(NodeInputSlot, input, this))
|
||||||
for (const [i, input] of this.inputs.entries()) {
|
for (const [i, input] of this.inputs.entries()) {
|
||||||
const link = this.graph && input.link != null
|
const link = this.graph && input.link != null
|
||||||
? this.graph._links.get(input.link)
|
? this.graph._links.get(input.link)
|
||||||
@@ -707,7 +711,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.outputs ??= []
|
this.outputs ??= []
|
||||||
this.outputs = this.outputs.map(output => toClass(NodeOutputSlot, output))
|
this.outputs = this.outputs.map(output => toClass(NodeOutputSlot, output, this))
|
||||||
for (const [i, output] of this.outputs.entries()) {
|
for (const [i, output] of this.outputs.entries()) {
|
||||||
if (!output.links) continue
|
if (!output.links) continue
|
||||||
|
|
||||||
@@ -1424,7 +1428,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
type: ISlotType,
|
type: ISlotType,
|
||||||
extra_info?: Partial<INodeOutputSlot>,
|
extra_info?: Partial<INodeOutputSlot>,
|
||||||
): INodeOutputSlot {
|
): INodeOutputSlot {
|
||||||
const output = new NodeOutputSlot({ name, type, links: null })
|
const output = new NodeOutputSlot({ name, type, links: null }, this)
|
||||||
if (extra_info) Object.assign(output, extra_info)
|
if (extra_info) Object.assign(output, extra_info)
|
||||||
|
|
||||||
this.outputs ||= []
|
this.outputs ||= []
|
||||||
@@ -1470,7 +1474,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
*/
|
*/
|
||||||
addInput(name: string, type: ISlotType, extra_info?: Partial<INodeInputSlot>): INodeInputSlot {
|
addInput(name: string, type: ISlotType, extra_info?: Partial<INodeInputSlot>): INodeInputSlot {
|
||||||
type = type || 0
|
type = type || 0
|
||||||
const input = new NodeInputSlot({ name: name, type: type, link: null })
|
const input = new NodeInputSlot({ name: name, type: type, link: null }, this)
|
||||||
if (extra_info) Object.assign(input, extra_info)
|
if (extra_info) Object.assign(input, extra_info)
|
||||||
|
|
||||||
this.inputs ||= []
|
this.inputs ||= []
|
||||||
@@ -3430,18 +3434,18 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
*/
|
*/
|
||||||
drawCollapsedSlots(ctx: CanvasRenderingContext2D): void {
|
drawCollapsedSlots(ctx: CanvasRenderingContext2D): void {
|
||||||
// if collapsed
|
// if collapsed
|
||||||
let input_slot: INodeInputSlot | null = null
|
let input_slot: NodeInputSlot | undefined
|
||||||
let output_slot: INodeOutputSlot | null = null
|
let output_slot: NodeOutputSlot | undefined
|
||||||
|
|
||||||
// get first connected slot to render
|
// get first connected slot to render
|
||||||
for (const slot of this.inputs ?? []) {
|
for (const slot of this.#concreteInputs) {
|
||||||
if (slot.link == null) {
|
if (slot.link == null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
input_slot = slot
|
input_slot = slot
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
for (const slot of this.outputs ?? []) {
|
for (const slot of this.#concreteOutputs) {
|
||||||
if (!slot.links || !slot.links.length) {
|
if (!slot.links || !slot.links.length) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -3452,7 +3456,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
if (input_slot) {
|
if (input_slot) {
|
||||||
const x = 0
|
const x = 0
|
||||||
const y = LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
const y = LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
||||||
toClass(NodeInputSlot, input_slot).drawCollapsed(ctx, {
|
input_slot.drawCollapsed(ctx, {
|
||||||
pos: [x, y],
|
pos: [x, y],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -3460,7 +3464,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
if (output_slot) {
|
if (output_slot) {
|
||||||
const x = this._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH
|
const x = this._collapsed_width ?? LiteGraph.NODE_COLLAPSED_WIDTH
|
||||||
const y = LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
const y = LiteGraph.NODE_TITLE_HEIGHT * -0.5
|
||||||
toClass(NodeOutputSlot, output_slot).drawCollapsed(ctx, {
|
output_slot.drawCollapsed(ctx, {
|
||||||
pos: [x, y],
|
pos: [x, y],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -3470,30 +3474,29 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
return [...this.inputs, ...this.outputs]
|
return [...this.inputs, ...this.outputs]
|
||||||
}
|
}
|
||||||
|
|
||||||
#measureSlot(slot: INodeSlot, slotIndex: number): void {
|
#measureSlot(slot: NodeInputSlot | NodeOutputSlot, slotIndex: number, isInput: boolean): void {
|
||||||
const isInput = isINodeInputSlot(slot)
|
|
||||||
const pos = isInput ? this.getInputPos(slotIndex) : this.getOutputPos(slotIndex)
|
const pos = isInput ? this.getInputPos(slotIndex) : this.getOutputPos(slotIndex)
|
||||||
|
|
||||||
slot.boundingRect[0] = pos[0] - this.pos[0] - LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
slot.boundingRect[0] = pos[0] - LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||||
slot.boundingRect[1] = pos[1] - this.pos[1] - LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
slot.boundingRect[1] = pos[1] - LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||||
slot.boundingRect[2] = LiteGraph.NODE_SLOT_HEIGHT
|
slot.boundingRect[2] = slot.isWidgetInputSlot ? BaseWidget.margin : LiteGraph.NODE_SLOT_HEIGHT
|
||||||
slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT
|
slot.boundingRect[3] = LiteGraph.NODE_SLOT_HEIGHT
|
||||||
}
|
}
|
||||||
|
|
||||||
#measureSlots(): ReadOnlyRect | null {
|
#measureSlots(): ReadOnlyRect | null {
|
||||||
const slots: INodeSlot[] = []
|
const slots: (NodeInputSlot | NodeOutputSlot)[] = []
|
||||||
|
|
||||||
for (const [slotIndex, slot] of this.inputs.entries()) {
|
for (const [slotIndex, slot] of this.#concreteInputs.entries()) {
|
||||||
// Unrecognized nodes (Nodes with error) has inputs but no widgets. Treat
|
// Unrecognized nodes (Nodes with error) has inputs but no widgets. Treat
|
||||||
// converted inputs as normal inputs.
|
// converted inputs as normal inputs.
|
||||||
/** Widget input slots are handled in {@link layoutWidgetInputSlots} */
|
/** Widget input slots are handled in {@link layoutWidgetInputSlots} */
|
||||||
if (this.widgets?.length && isWidgetInputSlot(slot)) continue
|
if (this.widgets?.length && isWidgetInputSlot(slot)) continue
|
||||||
|
|
||||||
this.#measureSlot(slot, slotIndex)
|
this.#measureSlot(slot, slotIndex, true)
|
||||||
slots.push(slot)
|
slots.push(slot)
|
||||||
}
|
}
|
||||||
for (const [slotIndex, slot] of this.outputs.entries()) {
|
for (const [slotIndex, slot] of this.#concreteOutputs.entries()) {
|
||||||
this.#measureSlot(slot, slotIndex)
|
this.#measureSlot(slot, slotIndex, false)
|
||||||
slots.push(slot)
|
slots.push(slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3542,9 +3545,8 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
editorAlpha,
|
editorAlpha,
|
||||||
lowQuality,
|
lowQuality,
|
||||||
}: DrawSlotsOptions) {
|
}: DrawSlotsOptions) {
|
||||||
for (const slot of this.slots) {
|
for (const slot of [...this.#concreteInputs, ...this.#concreteOutputs]) {
|
||||||
const slotInstance = toNodeSlotClass(slot)
|
const isValidTarget = fromSlot && slot.isValidTarget(fromSlot)
|
||||||
const isValidTarget = fromSlot && slotInstance.isValidTarget(fromSlot)
|
|
||||||
const isMouseOverSlot = this.#isMouseOverSlot(slot)
|
const isMouseOverSlot = this.#isMouseOverSlot(slot)
|
||||||
|
|
||||||
// change opacity of incompatible slots when dragging a connection
|
// change opacity of incompatible slots when dragging a connection
|
||||||
@@ -3559,12 +3561,12 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
if (
|
if (
|
||||||
isMouseOverSlot ||
|
isMouseOverSlot ||
|
||||||
isValidTarget ||
|
isValidTarget ||
|
||||||
!slotInstance.isWidgetInputSlot ||
|
!slot.isWidgetInputSlot ||
|
||||||
this.#isMouseOverWidget(this.getWidgetFromSlot(slotInstance)) ||
|
this.#isMouseOverWidget(this.getWidgetFromSlot(slot)) ||
|
||||||
slotInstance.isConnected()
|
slot.isConnected()
|
||||||
) {
|
) {
|
||||||
ctx.globalAlpha = isValid ? editorAlpha : 0.4 * editorAlpha
|
ctx.globalAlpha = isValid ? editorAlpha : 0.4 * editorAlpha
|
||||||
slotInstance.draw(ctx, {
|
slot.draw(ctx, {
|
||||||
colorContext,
|
colorContext,
|
||||||
lowQuality,
|
lowQuality,
|
||||||
highlight,
|
highlight,
|
||||||
@@ -3673,19 +3675,31 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
|||||||
const slot = slotByWidgetName.get(widget.name)
|
const slot = slotByWidgetName.get(widget.name)
|
||||||
if (!slot) continue
|
if (!slot) continue
|
||||||
|
|
||||||
const actualSlot = this.inputs[slot.index]
|
const actualSlot = this.#concreteInputs[slot.index]
|
||||||
const offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
const offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5
|
||||||
actualSlot.pos = [offset, widget.y + offset]
|
actualSlot.pos = [offset, widget.y + offset]
|
||||||
this.#measureSlot(actualSlot, slot.index)
|
this.#measureSlot(actualSlot, slot.index, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal Sets the internal concrete slot arrays, ensuring they are instances of
|
||||||
|
* {@link NodeInputSlot} or {@link NodeOutputSlot}.
|
||||||
|
*
|
||||||
|
* A temporary workaround until duck-typed inputs and outputs
|
||||||
|
* have been removed from the ecosystem.
|
||||||
|
*/
|
||||||
|
_setConcreteSlots(): void {
|
||||||
|
this.#concreteInputs = this.inputs.map(slot => toClass(NodeInputSlot, slot, this))
|
||||||
|
this.#concreteOutputs = this.outputs.map(slot => toClass(NodeOutputSlot, slot, this))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arranges node elements in preparation for rendering (slots & widgets).
|
* Arranges node elements in preparation for rendering (slots & widgets).
|
||||||
*/
|
*/
|
||||||
arrange(): void {
|
arrange(): void {
|
||||||
const slotsBounds = this.#measureSlots()
|
const slotsBounds = this.#measureSlots()
|
||||||
const widgetStartY = slotsBounds ? slotsBounds[1] + slotsBounds[3] : 0
|
const widgetStartY = slotsBounds ? slotsBounds[1] + slotsBounds[3] - this.pos[1] : 0
|
||||||
this.#arrangeWidgets(widgetStartY)
|
this.#arrangeWidgets(widgetStartY)
|
||||||
this.#arrangeWidgetInputSlots()
|
this.#arrangeWidgetInputSlots()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { INodeInputSlot, INodeOutputSlot, OptionalProps } from "@/interfaces"
|
import type { INodeInputSlot, INodeOutputSlot, OptionalProps } from "@/interfaces"
|
||||||
|
import type { LGraphNode } from "@/LGraphNode"
|
||||||
import type { LinkId } from "@/LLink"
|
import type { LinkId } from "@/LLink"
|
||||||
|
|
||||||
import { LabelPosition } from "@/draw"
|
import { LabelPosition } from "@/draw"
|
||||||
@@ -12,8 +13,8 @@ export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
|||||||
return !!this.widget
|
return !!this.widget
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(slot: OptionalProps<INodeInputSlot, "boundingRect">) {
|
constructor(slot: OptionalProps<INodeInputSlot, "boundingRect">, node: LGraphNode) {
|
||||||
super(slot)
|
super(slot, node)
|
||||||
this.link = slot.link
|
this.link = slot.link
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { INodeInputSlot, INodeOutputSlot, OptionalProps } from "@/interfaces"
|
import type { INodeInputSlot, INodeOutputSlot, OptionalProps } from "@/interfaces"
|
||||||
|
import type { LGraphNode } from "@/LGraphNode"
|
||||||
import type { LinkId } from "@/LLink"
|
import type { LinkId } from "@/LLink"
|
||||||
|
|
||||||
import { LabelPosition } from "@/draw"
|
import { LabelPosition } from "@/draw"
|
||||||
@@ -14,8 +15,8 @@ export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(slot: OptionalProps<INodeOutputSlot, "boundingRect">) {
|
constructor(slot: OptionalProps<INodeOutputSlot, "boundingRect">, node: LGraphNode) {
|
||||||
super(slot)
|
super(slot, node)
|
||||||
this.links = slot.links
|
this.links = slot.links
|
||||||
this._data = slot._data
|
this._data = slot._data
|
||||||
this.slot_index = slot.slot_index
|
this.slot_index = slot.slot_index
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { CanvasColour, Dictionary, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetLocator, OptionalProps, Point, Rect } from "@/interfaces"
|
import type { CanvasColour, Dictionary, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetLocator, OptionalProps, Point, ReadOnlyPoint, Rect } from "@/interfaces"
|
||||||
|
import type { LGraphNode } from "@/LGraphNode"
|
||||||
|
|
||||||
import { LabelPosition, SlotShape, SlotType } from "@/draw"
|
import { LabelPosition, SlotShape, SlotType } from "@/draw"
|
||||||
import { LiteGraph } from "@/litegraph"
|
import { LiteGraph } from "@/litegraph"
|
||||||
@@ -41,7 +42,28 @@ export abstract class NodeSlot implements INodeSlot {
|
|||||||
pos?: Point
|
pos?: Point
|
||||||
widget?: IWidgetLocator
|
widget?: IWidgetLocator
|
||||||
hasErrors?: boolean
|
hasErrors?: boolean
|
||||||
boundingRect: Rect
|
readonly boundingRect: Rect
|
||||||
|
|
||||||
|
/** The offset from the parent node to the centre point of this slot. */
|
||||||
|
get #centreOffset(): ReadOnlyPoint {
|
||||||
|
const nodePos = this.node.pos
|
||||||
|
const { boundingRect } = this
|
||||||
|
|
||||||
|
// Use height; widget input slots may be thinner.
|
||||||
|
const diameter = boundingRect[3]
|
||||||
|
|
||||||
|
return getCentre([
|
||||||
|
boundingRect[0] - nodePos[0],
|
||||||
|
boundingRect[1] - nodePos[1],
|
||||||
|
diameter,
|
||||||
|
diameter,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
#node: LGraphNode
|
||||||
|
get node(): LGraphNode {
|
||||||
|
return this.#node
|
||||||
|
}
|
||||||
|
|
||||||
get highlightColor(): CanvasColour {
|
get highlightColor(): CanvasColour {
|
||||||
return LiteGraph.NODE_TEXT_HIGHLIGHT_COLOR ?? LiteGraph.NODE_SELECTED_TITLE_COLOR ?? LiteGraph.NODE_TEXT_COLOR
|
return LiteGraph.NODE_TEXT_HIGHLIGHT_COLOR ?? LiteGraph.NODE_SELECTED_TITLE_COLOR ?? LiteGraph.NODE_TEXT_COLOR
|
||||||
@@ -49,11 +71,12 @@ export abstract class NodeSlot implements INodeSlot {
|
|||||||
|
|
||||||
abstract get isWidgetInputSlot(): boolean
|
abstract get isWidgetInputSlot(): boolean
|
||||||
|
|
||||||
constructor(slot: OptionalProps<INodeSlot, "boundingRect">) {
|
constructor(slot: OptionalProps<INodeSlot, "boundingRect">, node: LGraphNode) {
|
||||||
Object.assign(this, slot)
|
Object.assign(this, slot)
|
||||||
this.name = slot.name
|
this.name = slot.name
|
||||||
this.type = slot.type
|
this.type = slot.type
|
||||||
this.boundingRect = slot.boundingRect ?? [0, 0, 0, 0]
|
this.boundingRect = slot.boundingRect ?? [0, 0, 0, 0]
|
||||||
|
this.#node = node
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,7 +132,7 @@ export abstract class NodeSlot implements INodeSlot {
|
|||||||
? this.highlightColor
|
? this.highlightColor
|
||||||
: LiteGraph.NODE_TEXT_COLOR
|
: LiteGraph.NODE_TEXT_COLOR
|
||||||
|
|
||||||
const pos = getCentre(this.boundingRect)
|
const pos = this.#centreOffset
|
||||||
const slot_type = this.type
|
const slot_type = this.type
|
||||||
const slot_shape = (
|
const slot_shape = (
|
||||||
slot_type === SlotType.Array ? SlotShape.Grid : this.shape
|
slot_type === SlotType.Array ? SlotShape.Grid : this.shape
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ import type { IWidgetInputSlot, SharedIntersection } from "@/interfaces"
|
|||||||
import type { INodeInputSlot, INodeOutputSlot, INodeSlot, IWidget } from "@/litegraph"
|
import type { INodeInputSlot, INodeOutputSlot, INodeSlot, IWidget } from "@/litegraph"
|
||||||
import type { ISerialisableNodeInput, ISerialisableNodeOutput } from "@/types/serialisation"
|
import type { ISerialisableNodeInput, ISerialisableNodeOutput } from "@/types/serialisation"
|
||||||
|
|
||||||
import { NodeInputSlot } from "./NodeInputSlot"
|
|
||||||
import { NodeOutputSlot } from "./NodeOutputSlot"
|
|
||||||
|
|
||||||
type CommonIoSlotProps = SharedIntersection<ISerialisableNodeInput, ISerialisableNodeOutput>
|
type CommonIoSlotProps = SharedIntersection<ISerialisableNodeInput, ISerialisableNodeOutput>
|
||||||
|
|
||||||
export function shallowCloneCommonProps(slot: CommonIoSlotProps): CommonIoSlotProps {
|
export function shallowCloneCommonProps(slot: CommonIoSlotProps): CommonIoSlotProps {
|
||||||
@@ -57,11 +54,3 @@ export function isINodeOutputSlot(slot: INodeSlot): slot is INodeOutputSlot {
|
|||||||
export function isWidgetInputSlot(slot: INodeInputSlot): slot is IWidgetInputSlot {
|
export function isWidgetInputSlot(slot: INodeInputSlot): slot is IWidgetInputSlot {
|
||||||
return !!slot.widget
|
return !!slot.widget
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toNodeSlotClass(slot: INodeInputSlot | INodeOutputSlot): NodeInputSlot | NodeOutputSlot {
|
|
||||||
if (slot instanceof NodeInputSlot || slot instanceof NodeOutputSlot) return slot
|
|
||||||
|
|
||||||
return "link" in slot
|
|
||||||
? new NodeInputSlot(slot)
|
|
||||||
: new NodeOutputSlot(slot)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ import type { IColorable } from "@/interfaces"
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a plain object to a class instance if it is not already an instance of the class.
|
* Converts a plain object to a class instance if it is not already an instance of the class.
|
||||||
|
*
|
||||||
|
* Requires specific constructor signature; first parameter must be the object to convert.
|
||||||
* @param cls The class to convert to
|
* @param cls The class to convert to
|
||||||
* @param obj The object to convert
|
* @param args The object to convert, followed by any other constructor arguments
|
||||||
* @returns The class instance
|
* @returns The class instance
|
||||||
*/
|
*/
|
||||||
export function toClass<P, C>(cls: new (plain: P) => C, obj: P | C): C {
|
export function toClass<P, C extends P, Args extends unknown[]>(
|
||||||
return obj instanceof cls ? obj : new cls(obj as P)
|
cls: new (instance: P, ...args: Args) => C,
|
||||||
|
...args: [P, ...Args]
|
||||||
|
): C {
|
||||||
|
return args[0] instanceof cls ? args[0] : new cls(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user