Add UI code for configuring subgraphNode widgets (#5826)

The third PR for managing display of widgets on subgraph nodes. This is
the one that actually makes the functionality usable and user visible.

Adds
- A right-side modal for configuring which widgets are promoted,
accessed by right click or selection toolbar
- This menu allows for re-arranging widget order by dragging and
dropping.
- Indicators inside the subgraph for which widgets have been promoted.
- Context menu options for promoting or demoting widget inside of a
subgraph.
<img width="767" height="694" alt="image"
src="https://github.com/user-attachments/assets/4f78645d-7b26-48ba-8c49-78f4807e89e8"
/>
<img width="784" height="435" alt="image"
src="https://github.com/user-attachments/assets/7005c730-a732-481e-befb-57019a8a31a7"
/>


Known issues
- Some preview widgets are not added to a node until a draw operation
occurs. The code does not yet have a way of determining which nodes
should have draw operations forced to facilitate initial widget
creation.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-5826-Add-UI-code-for-configuring-subgraphNode-widgets-27c6d73d36508146accbf395e5bcd36a)
by [Unito](https://www.unito.io)
This commit is contained in:
AustinMroz
2025-10-02 17:19:47 -07:00
committed by GitHub
parent 48335475dc
commit 720de8cc8a
32 changed files with 795 additions and 154 deletions

View File

@@ -1862,13 +1862,13 @@ export class LGraphCanvas
this.#dirty()
}
openSubgraph(subgraph: Subgraph): void {
openSubgraph(subgraph: Subgraph, fromNode: SubgraphNode): void {
const { graph } = this
if (!graph) throw new NullGraphError()
const options = {
bubbles: true,
detail: { subgraph, closingGraph: graph },
detail: { subgraph, closingGraph: graph, fromNode },
cancelable: true
}
const mayContinue = this.canvas.dispatchEvent(
@@ -2794,7 +2794,7 @@ export class LGraphCanvas
if (pos[1] < 0 && !inCollapse) {
node.onNodeTitleDblClick?.(e, pos, this)
} else if (node instanceof SubgraphNode) {
this.openSubgraph(node.subgraph)
this.openSubgraph(node.subgraph, node)
}
node.onDblClick?.(e, pos, this)
@@ -8007,7 +8007,7 @@ export class LGraphCanvas
if (Object.keys(this.selected_nodes).length > 1) {
options.push(
{
content: 'Convert to Subgraph 🆕',
content: 'Convert to Subgraph',
callback: () => {
if (!this.selectedItems.size)
throw new Error('Convert to Subgraph: Nothing selected.')
@@ -8042,7 +8042,7 @@ export class LGraphCanvas
} else {
options = [
{
content: 'Convert to Subgraph 🆕',
content: 'Convert to Subgraph',
callback: () => {
// find groupnodes, degroup and select children
if (this.selectedItems.size) {

View File

@@ -3749,6 +3749,13 @@ export class LGraphNode
return !isHidden
}
updateComputedDisabled() {
if (!this.widgets) return
for (const widget of this.widgets)
widget.computedDisabled =
widget.disabled || this.getSlotFromWidget(widget)?.link != null
}
drawWidgets(
ctx: CanvasRenderingContext2D,
{ lowQuality = false, editorAlpha = 1 }: DrawWidgetsOptions
@@ -3762,6 +3769,7 @@ export class LGraphNode
ctx.save()
ctx.globalAlpha = editorAlpha
this.updateComputedDisabled()
for (const widget of widgets) {
if (!this.isWidgetVisible(widget)) continue
@@ -3771,9 +3779,6 @@ export class LGraphNode
: LiteGraph.WIDGET_OUTLINE_COLOR
widget.last_y = y
// Disable widget if it is disabled or if the value is passed from socket connection.
widget.computedDisabled =
widget.disabled || this.getSlotFromWidget(widget)?.link != null
ctx.strokeStyle = outlineColour
ctx.fillStyle = '#222'

View File

@@ -70,6 +70,7 @@ export class LiteGraphGlobal {
WIDGET_BGCOLOR = '#222'
WIDGET_OUTLINE_COLOR = '#666'
WIDGET_PROMOTED_OUTLINE_COLOR = '#BF00FF'
WIDGET_ADVANCED_OUTLINE_COLOR = 'rgba(56, 139, 253, 0.8)'
WIDGET_TEXT_COLOR = '#DDD'
WIDGET_SECONDARY_TEXT_COLOR = '#999'

View File

@@ -4,6 +4,7 @@ import type { LGraphGroup } from '@/lib/litegraph/src/LGraphGroup'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import type { ConnectingLink } from '@/lib/litegraph/src/interfaces'
import type { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph'
import type { SubgraphNode } from '@/lib/litegraph/src/subgraph/SubgraphNode'
import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
export interface LGraphCanvasEventMap {
@@ -14,6 +15,11 @@ export interface LGraphCanvasEventMap {
/** The old active graph, or `null` if there was no active graph. */
oldGraph: LGraph | Subgraph | null | undefined
}
'subgraph-opened': {
subgraph: Subgraph
closingGraph: LGraph
fromNode: SubgraphNode
}
'litegraph:canvas':
| { subType: 'before-change' | 'after-change' }

View File

@@ -168,7 +168,7 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
canvas: LGraphCanvas
): void {
if (button.name === 'enter_subgraph') {
canvas.openSubgraph(this.subgraph)
canvas.openSubgraph(this.subgraph, this)
} else {
super.onTitleButtonClick(button, canvas)
}

View File

@@ -308,6 +308,13 @@ export interface IBaseWidget<
hidden?: boolean
advanced?: boolean
/**
* This property is automatically computed on graph change
* and should not be changed.
* Promoted widgets have a colored border
* @see /core/graph/subgraph/proxyWidget.registerProxyWidgets
*/
promoted?: boolean
tooltip?: string

View File

@@ -74,6 +74,7 @@ export abstract class BaseWidget<TWidget extends IBaseWidget = IBaseWidget>
computedDisabled?: boolean
hidden?: boolean
advanced?: boolean
promoted?: boolean
tooltip?: string
element?: HTMLElement
callback?(
@@ -146,6 +147,7 @@ export abstract class BaseWidget<TWidget extends IBaseWidget = IBaseWidget>
}
get outline_color() {
if (this.promoted) return LiteGraph.WIDGET_PROMOTED_OUTLINE_COLOR
return this.advanced
? LiteGraph.WIDGET_ADVANCED_OUTLINE_COLOR
: LiteGraph.WIDGET_OUTLINE_COLOR