Group update - titlebar, resize, config (#270)

* Backport group header from frontend

* nit - TS types & redundant code

* Refactor - simplify group resize

* Fix group resize can be inverted

* Move group resize check to group class

* Add config for group padding & default colour

* nit - Remove redundant code
This commit is contained in:
filtered
2024-11-04 10:27:41 +11:00
committed by GitHub
parent 3be1937c41
commit ce8d39f650
2 changed files with 64 additions and 38 deletions

View File

@@ -260,6 +260,9 @@ export class LGraphCanvas {
selected_nodes: Dictionary<LGraphNode> = {} selected_nodes: Dictionary<LGraphNode> = {}
/** All selected nodes, groups, and reroutes */ /** All selected nodes, groups, and reroutes */
selectedItems: Set<Positionable> = new Set() selectedItems: Set<Positionable> = new Set()
/** The group currently being resized. */
resizingGroup: LGraphGroup | null = null
/** @deprecated See {@link LGraphCanvas.selectedItems} */
selected_group: LGraphGroup | null = null selected_group: LGraphGroup | null = null
visible_nodes: LGraphNode[] = [] visible_nodes: LGraphNode[] = []
node_dragged?: LGraphNode node_dragged?: LGraphNode
@@ -299,6 +302,7 @@ export class LGraphCanvas {
block_click?: boolean block_click?: boolean
last_click_position?: Point last_click_position?: Point
resizing_node?: LGraphNode resizing_node?: LGraphNode
/** @deprecated See {@link LGraphCanvas.resizingGroup} */
selected_group_resizing?: boolean selected_group_resizing?: boolean
last_mouse_dragging: boolean last_mouse_dragging: boolean
onMouseDown: (arg0: CanvasMouseEvent) => void onMouseDown: (arg0: CanvasMouseEvent) => void
@@ -1253,7 +1257,6 @@ export class LGraphCanvas {
this.dragging_rectangle = null this.dragging_rectangle = null
this.selected_nodes = {} this.selected_nodes = {}
/** The group currently being resized */
this.selected_group = null this.selected_group = null
this.visible_nodes = [] this.visible_nodes = []
@@ -1428,13 +1431,14 @@ export class LGraphCanvas {
if (!skip_events) this.bindEvents() if (!skip_events) this.bindEvents()
} }
//used in some events to capture them /** Captures an event and prevents default - returns false. */
_doNothing(e: Event) { _doNothing(e: Event): boolean {
//console.log("pointerevents: _doNothing "+e.type); //console.log("pointerevents: _doNothing "+e.type);
e.preventDefault() e.preventDefault()
return false return false
} }
_doReturnTrue(e: Event) { /** Captures an event and prevents default - returns true. */
_doReturnTrue(e: Event): boolean {
e.preventDefault() e.preventDefault()
return true return true
} }
@@ -1722,7 +1726,6 @@ export class LGraphCanvas {
let node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes) let node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes)
let skip_action = false let skip_action = false
const now = LiteGraph.getTime() const now = LiteGraph.getTime()
const is_primary = (e.isPrimary === undefined || !e.isPrimary)
const is_double_click = (now - this.last_mouseclick < 300) const is_double_click = (now - this.last_mouseclick < 300)
this.mouse[0] = e.clientX this.mouse[0] = e.clientX
this.mouse[1] = e.clientY this.mouse[1] = e.clientY
@@ -1730,7 +1733,7 @@ export class LGraphCanvas {
this.graph_mouse[1] = e.canvasY this.graph_mouse[1] = e.canvasY
this.last_click_position = [this.mouse[0], this.mouse[1]] this.last_click_position = [this.mouse[0], this.mouse[1]]
this.pointer_is_double = this.pointer_is_down && is_primary this.pointer_is_double = this.pointer_is_down && e.isPrimary
this.pointer_is_down = true this.pointer_is_down = true
this.canvas.focus() this.canvas.focus()
@@ -2057,11 +2060,8 @@ export class LGraphCanvas {
this.ctx.lineWidth = lineWidth this.ctx.lineWidth = lineWidth
} }
const group = this.graph.getGroupOnPos(e.canvasX, e.canvasY)
this.selected_group = this.graph.getGroupOnPos(e.canvasX, e.canvasY) this.selected_group = group
this.selected_group_resizing = false
const group = this.selected_group
if (group && !this.read_only) { if (group && !this.read_only) {
if (e.ctrlKey) { if (e.ctrlKey) {
this.dragging_rectangle = null this.dragging_rectangle = null
@@ -2069,7 +2069,7 @@ export class LGraphCanvas {
const dist = distance([e.canvasX, e.canvasY], [group.pos[0] + group.size[0], group.pos[1] + group.size[1]]) const dist = distance([e.canvasX, e.canvasY], [group.pos[0] + group.size[0], group.pos[1] + group.size[1]])
if (dist * this.ds.scale < 10) { if (dist * this.ds.scale < 10) {
this.selected_group_resizing = true this.resizingGroup = group
} else { } else {
const f = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE const f = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE
const headerHeight = f * 1.4 const headerHeight = f * 1.4
@@ -2086,7 +2086,7 @@ export class LGraphCanvas {
this.emitEvent({ this.emitEvent({
subType: "group-double-click", subType: "group-double-click",
originalEvent: e, originalEvent: e,
group: this.selected_group, group,
}) })
} }
} else if (is_double_click && !this.read_only) { } else if (is_double_click && !this.read_only) {
@@ -2259,19 +2259,20 @@ export class LGraphCanvas {
//get node over //get node over
const node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes) const node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes)
const { resizingGroup } = this
if (this.dragging_rectangle) { if (this.dragging_rectangle) {
this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0] this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]
this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1] this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]
this.dirty_canvas = true this.dirty_canvas = true
} }
else if (this.selected_group_resizing && !this.read_only) { else if (resizingGroup && !this.read_only) {
//moving/resizing a group // Resizing a group
this.selected_group.resize( const resized = resizingGroup.resize(
e.canvasX - this.selected_group.pos[0], e.canvasX - resizingGroup.pos[0],
e.canvasY - this.selected_group.pos[1] e.canvasY - resizingGroup.pos[1]
) )
this.dirty_bgcanvas = true if (resized) this.dirty_bgcanvas = true
} else if (this.dragging_canvas) { } else if (this.dragging_canvas) {
this.ds.offset[0] += delta[0] / this.ds.scale this.ds.offset[0] += delta[0] / this.ds.scale
this.ds.offset[1] += delta[1] / this.ds.scale this.ds.offset[1] += delta[1] / this.ds.scale
@@ -2494,6 +2495,7 @@ export class LGraphCanvas {
this.block_click &&= false this.block_click &&= false
if (e.which == 1) { if (e.which == 1) {
this.resizingGroup = null
if (this.node_widget) { if (this.node_widget) {
this.processNodeWidgets(this.node_widget[0], this.graph_mouse, e) this.processNodeWidgets(this.node_widget[0], this.graph_mouse, e)
@@ -2501,12 +2503,7 @@ export class LGraphCanvas {
//left button //left button
this.node_widget = null this.node_widget = null
this.selected_group = null
if (this.selected_group) {
this.dirty_canvas = true
this.selected_group = null
}
this.selected_group_resizing = false
this.isDragging = false this.isDragging = false
let node = this.graph.getNodeOnPos( let node = this.graph.getNodeOnPos(

View File

@@ -12,12 +12,18 @@ export interface IGraphGroupFlags extends Record<string, unknown> {
} }
export class LGraphGroup implements Positionable { export class LGraphGroup implements Positionable {
static minWidth = 140
static minHeight = 80
static resizeLength = 10
static padding = 4
static defaultColour = '#335'
id: number id: number
color: string color: string
title: string title: string
font?: string font?: string
font_size: number = LiteGraph.DEFAULT_GROUP_FONT || 24 font_size: number = LiteGraph.DEFAULT_GROUP_FONT || 24
_bounding: Float32Array = new Float32Array([10, 10, 140, 80]) _bounding: Float32Array = new Float32Array([10, 10, LGraphGroup.minWidth, LGraphGroup.minHeight])
_pos: Point = this._bounding.subarray(0, 2) _pos: Point = this._bounding.subarray(0, 2)
_size: Size = this._bounding.subarray(2, 4) _size: Size = this._bounding.subarray(2, 4)
/** @deprecated See {@link _children} */ /** @deprecated See {@link _children} */
@@ -54,10 +60,11 @@ export class LGraphGroup implements Positionable {
set size(v) { set size(v) {
if (!v || v.length < 2) return if (!v || v.length < 2) return
this._size[0] = Math.max(140, v[0]) this._size[0] = Math.max(LGraphGroup.minWidth, v[0])
this._size[1] = Math.max(80, v[1]) this._size[1] = Math.max(LGraphGroup.minHeight, v[1])
} }
get boundingRect() { get boundingRect() {
return this._bounding return this._bounding
} }
@@ -113,26 +120,37 @@ export class LGraphGroup implements Positionable {
* @param {CanvasRenderingContext2D} ctx * @param {CanvasRenderingContext2D} ctx
*/ */
draw(graphCanvas: LGraphCanvas, ctx: CanvasRenderingContext2D): void { draw(graphCanvas: LGraphCanvas, ctx: CanvasRenderingContext2D): void {
const padding = 4 const { padding, resizeLength, defaultColour } = LGraphGroup
const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE
ctx.fillStyle = this.color
ctx.strokeStyle = this.color
const [x, y] = this._pos const [x, y] = this._pos
const [width, height] = this._size const [width, height] = this._size
// Titlebar
ctx.globalAlpha = 0.25 * graphCanvas.editor_alpha ctx.globalAlpha = 0.25 * graphCanvas.editor_alpha
ctx.fillStyle = this.color || defaultColour
ctx.strokeStyle = this.color || defaultColour
ctx.beginPath()
ctx.rect(x + 0.5, y + 0.5, width, font_size * 1.4)
ctx.fill()
// Group background, border
ctx.fillStyle = this.color
ctx.strokeStyle = this.color
ctx.beginPath() ctx.beginPath()
ctx.rect(x + 0.5, y + 0.5, width, height) ctx.rect(x + 0.5, y + 0.5, width, height)
ctx.fill() ctx.fill()
ctx.globalAlpha = graphCanvas.editor_alpha ctx.globalAlpha = graphCanvas.editor_alpha
ctx.stroke() ctx.stroke()
// Resize marker
ctx.beginPath() ctx.beginPath()
ctx.moveTo(x + width, y + height) ctx.moveTo(x + width, y + height)
ctx.lineTo(x + width - 10, y + height) ctx.lineTo(x + width - resizeLength, y + height)
ctx.lineTo(x + width, y + height - 10) ctx.lineTo(x + width, y + height - resizeLength)
ctx.fill() ctx.fill()
const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE // Title
ctx.font = font_size + "px Arial" ctx.font = font_size + "px Arial"
ctx.textAlign = "left" ctx.textAlign = "left"
ctx.fillText(this.title + (this.pinned ? "📌" : ""), x + padding, y + font_size) ctx.fillText(this.title + (this.pinned ? "📌" : ""), x + padding, y + font_size)
@@ -148,11 +166,12 @@ export class LGraphGroup implements Positionable {
} }
} }
resize(width: number, height: number): void { resize(width: number, height: number): boolean {
if (this.pinned) return if (this.pinned) return false
this._size[0] = width this._size[0] = Math.max(LGraphGroup.minWidth, width)
this._size[1] = height this._size[1] = Math.max(LGraphGroup.minHeight, height)
return true
} }
move(deltaX: number, deltaY: number, skipChildren: boolean = false): void { move(deltaX: number, deltaY: number, skipChildren: boolean = false): void {
@@ -264,6 +283,16 @@ export class LGraphGroup implements Positionable {
return isInsideRectangle(x, y, b[0], b[1], b[2], this.titleHeight) return isInsideRectangle(x, y, b[0], b[1], b[2], this.titleHeight)
} }
isInResize(x: number, y: number): boolean {
const b = this._bounding
const right = b[0] + b[2]
const bottom = b[1] + b[3]
return x < right
&& y < bottom
&& (x - right) + (y - bottom) > -LGraphGroup.resizeLength
}
isPointInside = LGraphNode.prototype.isPointInside isPointInside = LGraphNode.prototype.isPointInside
setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas
} }