mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-25 09:14:25 +00:00
Add CanvasPointer API (#308)
* Add position rounding feature Replaces previous impls. which only worked on some items, and were triggered when unexpected e.g. clicking a node that hadn't been moved. Update test expectations * Narrow TS types - readonly * nit - Clean up, Doc * nit - Clean up legacy accessors Marks as deprecated * Fix TS type - IContextMenuOptions.scale * [Refactor] dist2 for use in pointer API * Add CanvasPointer - API for pointer events Add TS strict types Add final click drag distance math Add option to retain events * nit - Rename * nit * Remove Subgraph - unused & not maintained * Remove live_mode Unused, not maintained. * Update README Remove live_mode reference * Update delete selected - include reroutes & groups * Bypass link menu if alt/shift pressed * Remove old dragged_node interface Incomplete impl. - unused. Superceded by selectedItems * Fix top/left edge of rectangles not in hitbox * [Refactor] Match function names to interface names * Add interface to find widgets by Point LGraphNode.getWidgetOnPos * Add widget search param - includeDisabled * nit - Doc * Rewrite canvas mouse handling - Rewrites most pointer handling to use CanvasPointer callbacks - All callbacks are declared ahead of time during the initial pointerdown event, logically grouped together - Drastically simplifies the alteration or creation of new click / drag interactions - Click events are all clicks, rather than some processed on mouse down, others on mouse up - Functions return instead of setting and repeatedly checking multiple state vars - Removes all lines that needed THIRTEEN tab indents * Split middle click out from processMouseDown * Use pointer API for link menus * Narrow canvas event interfaces * Fix canvas event types Replaces original workarounds with final types * Refactor - deprecated isInsideRectangle * Add canvas hovering over state - Centralises cursor set behaviour - Provides simple downstream override * nit * [Refactor] Use measure functions * Add double-click API to CanvasPointer a * nit - Doc * Allow larger gap between double click events * Rewrite double-click into CanvasPointer actions * Improve double-click UX Prefer down events over up events * Add production defaults * Add middle-click handling * Remove debug code * Remove redundant code * Fix add reroute alt-click adds two undo steps * Fix click on connected input disconnects Old behaviour was to disconnect, then recreate a new link on drop. * Add module export: CanvasPointer
This commit is contained in:
@@ -9,7 +9,7 @@ import type { Reroute, RerouteId } from "./Reroute"
|
||||
import { LGraphEventMode, NodeSlotType, TitleMode, RenderShape } from "./types/globalEnums"
|
||||
import { BadgePosition, LGraphBadge } from "./LGraphBadge"
|
||||
import { type LGraphNodeConstructor, LiteGraph } from "./litegraph"
|
||||
import { isInsideRectangle, isXyInRectangle } from "./measure"
|
||||
import { isInRectangle, isInRect } from "./measure"
|
||||
import { LLink } from "./LLink"
|
||||
|
||||
export type NodeId = number | string
|
||||
@@ -322,8 +322,11 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
onGetOutputs?(this: LGraphNode): INodeOutputSlot[]
|
||||
onMouseUp?(this: LGraphNode, e: CanvasMouseEvent, pos: Point): void
|
||||
onMouseEnter?(this: LGraphNode, e: CanvasMouseEvent): void
|
||||
/** Blocks drag if return value is truthy. @param pos Offset from {@link LGraphNode.pos}. */
|
||||
onMouseDown?(this: LGraphNode, e: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): boolean
|
||||
/** @param pos Offset from {@link LGraphNode.pos}. */
|
||||
onDblClick?(this: LGraphNode, e: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): void
|
||||
/** @param pos Offset from {@link LGraphNode.pos}. */
|
||||
onNodeTitleDblClick?(this: LGraphNode, e: CanvasMouseEvent, pos: Point, canvas: LGraphCanvas): void
|
||||
onDrawTitle?(this: LGraphNode, ctx: CanvasRenderingContext2D): void
|
||||
onDrawTitleText?(this: LGraphNode, ctx: CanvasRenderingContext2D, title_height: number, size: Size, scale: number, title_text_font: string, selected: boolean): void
|
||||
@@ -1330,7 +1333,7 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
inResizeCorner(canvasX: number, canvasY: number): boolean {
|
||||
const rows = this.outputs ? this.outputs.length : 1
|
||||
const outputs_offset = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT
|
||||
return isInsideRectangle(canvasX,
|
||||
return isInRectangle(canvasX,
|
||||
canvasY,
|
||||
this.pos[0] + this.size[0] - 15,
|
||||
this.pos[1] + Math.max(this.size[1] - 15, outputs_offset),
|
||||
@@ -1518,7 +1521,7 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
* @return {boolean}
|
||||
*/
|
||||
isPointInside(x: number, y: number): boolean {
|
||||
return isXyInRectangle(x, y, this.boundingRect)
|
||||
return isInRect(x, y, this.boundingRect)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1529,7 +1532,7 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
*/
|
||||
isPointInCollapse(x: number, y: number): boolean {
|
||||
const squareLength = LiteGraph.NODE_TITLE_HEIGHT
|
||||
return isInsideRectangle(x, y, this.pos[0], this.pos[1] - squareLength, squareLength, squareLength)
|
||||
return isInRectangle(x, y, this.pos[0], this.pos[1] - squareLength, squareLength, squareLength)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1545,7 +1548,7 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
for (let i = 0, l = this.inputs.length; i < l; ++i) {
|
||||
const input = this.inputs[i]
|
||||
this.getConnectionPos(true, i, link_pos)
|
||||
if (isInsideRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20, 10)) {
|
||||
if (isInRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20, 10)) {
|
||||
return { input, slot: i, link_pos }
|
||||
}
|
||||
}
|
||||
@@ -1555,7 +1558,7 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
for (let i = 0, l = this.outputs.length; i < l; ++i) {
|
||||
const output = this.outputs[i]
|
||||
this.getConnectionPos(false, i, link_pos)
|
||||
if (isInsideRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20, 10)) {
|
||||
if (isInRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20, 10)) {
|
||||
return { output, slot: i, link_pos }
|
||||
}
|
||||
}
|
||||
@@ -1564,6 +1567,36 @@ export class LGraphNode implements Positionable, IPinnable {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the widget on this node at the given co-ordinates.
|
||||
* @param canvasX X co-ordinate in graph space
|
||||
* @param canvasY Y co-ordinate in graph space
|
||||
* @returns The widget found, otherwise `null`
|
||||
*/
|
||||
getWidgetOnPos(canvasX: number, canvasY: number, includeDisabled = false): IWidget | null {
|
||||
const { widgets, pos, size } = this
|
||||
if (!widgets?.length) return null
|
||||
|
||||
const x = canvasX - pos[0]
|
||||
const y = canvasY - pos[1]
|
||||
const nodeWidth = size[0]
|
||||
|
||||
for (const widget of widgets) {
|
||||
if (!widget || (widget.disabled && !includeDisabled) || widget.hidden || (widget.advanced && !this.showAdvanced)) continue
|
||||
|
||||
const h = widget.computeSize
|
||||
? widget.computeSize(nodeWidth)[1]
|
||||
: LiteGraph.NODE_WIDGET_HEIGHT
|
||||
const w = widget.width || nodeWidth
|
||||
if (
|
||||
widget.last_y !== undefined &&
|
||||
isInRectangle(x, y, 6, widget.last_y, w - 12, h)
|
||||
)
|
||||
return widget
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input slot with a given name (used for dynamic slots), -1 if not found
|
||||
* @param name the name of the slot
|
||||
|
||||
Reference in New Issue
Block a user