Overhaul node computeSize - use actual text width (#962)

### Node resize overhaul

- Precisely calculates node minimum size
- Prevents input & output overlap
- Prevents (normal*) widgets from rendering text over the edge of nodes
- Performance impact was sub-millisecond for normal usage in a 500-node
graph


![image](https://github.com/user-attachments/assets/5b6a6cc7-a752-4d7e-bcdf-b4bc8df26c51)

_Minimum size for a few example node configurations_

### Widgets

- Converts hard-coded draw render values to class static properties
- Adds widget button draw function for left/right arrow widgets

_*_ Exception: `control_after_generate`, as it is not a true input /
widget. A check may be added later to handle this special case.
This commit is contained in:
filtered
2025-04-25 00:31:34 +10:00
committed by GitHub
parent 79ce3199d4
commit 080a8da8f4
10 changed files with 97 additions and 52 deletions

View File

@@ -45,6 +45,7 @@ import {
import { findFreeSlotOfType } from "./utils/collections"
import { distributeSpace } from "./utils/spaceDistribution"
import { toClass } from "./utils/type"
import { BaseWidget } from "./widgets/BaseWidget"
import { WIDGET_TYPE_MAP } from "./widgets/widgetMap"
// #region Types
@@ -1529,7 +1530,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
const ctorSize = this.constructor.size
if (ctorSize) return [ctorSize[0], ctorSize[1]]
const { inputs, outputs } = this
const { inputs, outputs, widgets } = this
let rows = Math.max(
inputs ? inputs.filter(input => !isWidgetInputSlot(input)).length : 1,
outputs ? outputs.length : 1,
@@ -1539,45 +1540,66 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
// although it should be graphcanvas.inner_text_font size
const font_size = LiteGraph.NODE_TEXT_SIZE
const title_width = compute_text_size(this.title)
const padLeft = LiteGraph.NODE_TITLE_HEIGHT
const padRight = padLeft * 0.33
const title_width = padLeft + compute_text_size(this.title, this.titleFontStyle) + padRight
let input_width = 0
let widgetWidth = 0
let output_width = 0
if (inputs) {
for (const input of inputs) {
const text = input.label || input.localized_name || input.name || ""
const text_width = compute_text_size(text)
if (input_width < text_width)
input_width = text_width
const text_width = compute_text_size(text, this.innerFontStyle)
if (isWidgetInputSlot(input)) {
const widget = this.getWidgetFromSlot(input)
if (widget && !this.isWidgetVisible(widget)) continue
if (text_width > widgetWidth) widgetWidth = text_width
} else {
if (text_width > input_width) input_width = text_width
}
}
}
if (outputs) {
for (const output of outputs) {
const text = output.label || output.localized_name || output.name || ""
const text_width = compute_text_size(text)
const text_width = compute_text_size(text, this.innerFontStyle)
if (output_width < text_width)
output_width = text_width
}
}
size[0] = Math.max(input_width + output_width + 10, title_width)
size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH)
if (this.widgets?.length)
size[0] = Math.max(size[0], LiteGraph.NODE_WIDTH * 1.5)
const minWidth = LiteGraph.NODE_WIDTH * (widgets?.length ? 1.5 : 1)
// Text + slot width + centre padding
const centrePadding = input_width && output_width ? 5 : 0
const slotsWidth = input_width + output_width + (2 * LiteGraph.NODE_SLOT_HEIGHT) + centrePadding
// Total distance from edge of node to the inner edge of the widget 'previous' arrow button
const widgetMargin = BaseWidget.margin + BaseWidget.arrowMargin + BaseWidget.arrowWidth
const widgetPadding = BaseWidget.minValueWidth + (2 * widgetMargin)
if (widgetWidth) widgetWidth += widgetPadding
size[0] = Math.max(slotsWidth, widgetWidth, title_width, minWidth)
size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT
// Get widget height & expand size if necessary
let widgets_height = 0
if (this.widgets?.length) {
for (const widget of this.widgets) {
if (widgets?.length) {
for (const widget of widgets) {
if (!this.isWidgetVisible(widget)) continue
let widget_height = 0
if (widget.computeSize) {
widget_height += widget.computeSize(size[0])[1]
} else if (widget.computeLayoutSize) {
widget_height += widget.computeLayoutSize(this).minHeight
// Expand widget width if necessary
const { minHeight, minWidth } = widget.computeLayoutSize(this)
const widgetWidth = minWidth + widgetPadding
if (widgetWidth > size[0]) size[0] = widgetWidth
widget_height += minHeight
} else {
widget_height += LiteGraph.NODE_WIDGET_HEIGHT
}
@@ -1594,10 +1616,9 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
else
size[1] += widgets_height
function compute_text_size(text: string) {
return text
? font_size * text.length * 0.6
: 0
function compute_text_size(text: string, fontStyle: string) {
return LGraphCanvas._measureText?.(text, fontStyle) ??
font_size * (text?.length ?? 0) * 0.6
}
if (this.constructor.min_height && size[1] < this.constructor.min_height) {
@@ -3375,7 +3396,7 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
const show_text = !lowQuality
ctx.save()
ctx.globalAlpha = editorAlpha
const margin = 15
const { margin } = BaseWidget
for (const w of widgets) {
if (!this.isWidgetVisible(w)) continue