From ce8d39f6505006976ae196273d0234271bd54a06 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:27:41 +1100 Subject: [PATCH] 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 --- src/LGraphCanvas.ts | 47 ++++++++++++++++++-------------------- src/LGraphGroup.ts | 55 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/LGraphCanvas.ts b/src/LGraphCanvas.ts index 60f0cafb80..7bf5fcb7ba 100644 --- a/src/LGraphCanvas.ts +++ b/src/LGraphCanvas.ts @@ -260,6 +260,9 @@ export class LGraphCanvas { selected_nodes: Dictionary = {} /** All selected nodes, groups, and reroutes */ selectedItems: Set = new Set() + /** The group currently being resized. */ + resizingGroup: LGraphGroup | null = null + /** @deprecated See {@link LGraphCanvas.selectedItems} */ selected_group: LGraphGroup | null = null visible_nodes: LGraphNode[] = [] node_dragged?: LGraphNode @@ -299,6 +302,7 @@ export class LGraphCanvas { block_click?: boolean last_click_position?: Point resizing_node?: LGraphNode + /** @deprecated See {@link LGraphCanvas.resizingGroup} */ selected_group_resizing?: boolean last_mouse_dragging: boolean onMouseDown: (arg0: CanvasMouseEvent) => void @@ -1253,7 +1257,6 @@ export class LGraphCanvas { this.dragging_rectangle = null this.selected_nodes = {} - /** The group currently being resized */ this.selected_group = null this.visible_nodes = [] @@ -1428,13 +1431,14 @@ export class LGraphCanvas { if (!skip_events) this.bindEvents() } - //used in some events to capture them - _doNothing(e: Event) { + /** Captures an event and prevents default - returns false. */ + _doNothing(e: Event): boolean { //console.log("pointerevents: _doNothing "+e.type); e.preventDefault() return false } - _doReturnTrue(e: Event) { + /** Captures an event and prevents default - returns true. */ + _doReturnTrue(e: Event): boolean { e.preventDefault() return true } @@ -1722,7 +1726,6 @@ export class LGraphCanvas { let node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes) let skip_action = false const now = LiteGraph.getTime() - const is_primary = (e.isPrimary === undefined || !e.isPrimary) const is_double_click = (now - this.last_mouseclick < 300) this.mouse[0] = e.clientX this.mouse[1] = e.clientY @@ -1730,7 +1733,7 @@ export class LGraphCanvas { this.graph_mouse[1] = e.canvasY 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.canvas.focus() @@ -2057,11 +2060,8 @@ export class LGraphCanvas { this.ctx.lineWidth = lineWidth } - - this.selected_group = this.graph.getGroupOnPos(e.canvasX, e.canvasY) - this.selected_group_resizing = false - - const group = this.selected_group + const group = this.graph.getGroupOnPos(e.canvasX, e.canvasY) + this.selected_group = group if (group && !this.read_only) { if (e.ctrlKey) { 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]]) if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true + this.resizingGroup = group } else { const f = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE const headerHeight = f * 1.4 @@ -2086,7 +2086,7 @@ export class LGraphCanvas { this.emitEvent({ subType: "group-double-click", originalEvent: e, - group: this.selected_group, + group, }) } } else if (is_double_click && !this.read_only) { @@ -2259,19 +2259,20 @@ export class LGraphCanvas { //get node over const node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes) + const { resizingGroup } = this if (this.dragging_rectangle) { this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0] this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1] this.dirty_canvas = true } - else if (this.selected_group_resizing && !this.read_only) { - //moving/resizing a group - this.selected_group.resize( - e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] + else if (resizingGroup && !this.read_only) { + // Resizing a group + const resized = resizingGroup.resize( + e.canvasX - resizingGroup.pos[0], + e.canvasY - resizingGroup.pos[1] ) - this.dirty_bgcanvas = true + if (resized) this.dirty_bgcanvas = true } else if (this.dragging_canvas) { this.ds.offset[0] += delta[0] / this.ds.scale this.ds.offset[1] += delta[1] / this.ds.scale @@ -2494,6 +2495,7 @@ export class LGraphCanvas { this.block_click &&= false if (e.which == 1) { + this.resizingGroup = null if (this.node_widget) { this.processNodeWidgets(this.node_widget[0], this.graph_mouse, e) @@ -2501,12 +2503,7 @@ export class LGraphCanvas { //left button this.node_widget = null - - if (this.selected_group) { - this.dirty_canvas = true - this.selected_group = null - } - this.selected_group_resizing = false + this.selected_group = null this.isDragging = false let node = this.graph.getNodeOnPos( diff --git a/src/LGraphGroup.ts b/src/LGraphGroup.ts index d9cd7da4d4..6f096ee1ae 100644 --- a/src/LGraphGroup.ts +++ b/src/LGraphGroup.ts @@ -12,12 +12,18 @@ export interface IGraphGroupFlags extends Record { } export class LGraphGroup implements Positionable { + static minWidth = 140 + static minHeight = 80 + static resizeLength = 10 + static padding = 4 + static defaultColour = '#335' + id: number color: string title: string font?: string 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) _size: Size = this._bounding.subarray(2, 4) /** @deprecated See {@link _children} */ @@ -54,10 +60,11 @@ export class LGraphGroup implements Positionable { set size(v) { if (!v || v.length < 2) return - this._size[0] = Math.max(140, v[0]) - this._size[1] = Math.max(80, v[1]) + this._size[0] = Math.max(LGraphGroup.minWidth, v[0]) + this._size[1] = Math.max(LGraphGroup.minHeight, v[1]) } + get boundingRect() { return this._bounding } @@ -113,26 +120,37 @@ export class LGraphGroup implements Positionable { * @param {CanvasRenderingContext2D} ctx */ 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 [width, height] = this._size + + // Titlebar 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.rect(x + 0.5, y + 0.5, width, height) ctx.fill() ctx.globalAlpha = graphCanvas.editor_alpha ctx.stroke() + // Resize marker ctx.beginPath() ctx.moveTo(x + width, y + height) - ctx.lineTo(x + width - 10, y + height) - ctx.lineTo(x + width, y + height - 10) + ctx.lineTo(x + width - resizeLength, y + height) + ctx.lineTo(x + width, y + height - resizeLength) ctx.fill() - const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE + // Title ctx.font = font_size + "px Arial" ctx.textAlign = "left" 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 { - if (this.pinned) return + resize(width: number, height: number): boolean { + if (this.pinned) return false - this._size[0] = width - this._size[1] = height + this._size[0] = Math.max(LGraphGroup.minWidth, width) + this._size[1] = Math.max(LGraphGroup.minHeight, height) + return true } 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) } + 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 setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas }