@@ -56,11 +56,39 @@ export interface FlowNodeStyle {
5656 portText ?: TextStyleConfig ;
5757}
5858
59+ export enum MessageType {
60+ Info = "info" ,
61+ Warning = "warning" ,
62+ Error = "error"
63+ } ;
64+
65+ function MessageTypeColor ( messageType : MessageType ) : string {
66+ switch ( messageType ) {
67+ case MessageType . Info :
68+ return Theme . Node . Message . InfoColor ;
69+
70+ case MessageType . Warning :
71+ return Theme . Node . Message . WarnColor ;
72+
73+ case MessageType . Error :
74+ return Theme . Node . Message . ErrorColor ;
75+
76+ default :
77+ throw new Error ( `unrecognized message type ${ messageType } ` )
78+ }
79+ }
80+
81+ export interface NodeMessageConfig {
82+ message : string ;
83+ type ?: MessageType ;
84+ }
85+
5986export interface FlowNodeConfig {
6087 position ?: Vector2 ;
6188 title ?: string ;
6289 subTitle ?: string ;
6390 info ?: string ;
91+ messages ?: Array < NodeMessageConfig >
6492 locked ?: boolean ;
6593 data ?: Metadata ;
6694 contextMenu ?: ContextMenuConfig ;
@@ -111,6 +139,29 @@ export interface NodeIntersection {
111139
112140}
113141
142+ class MessageRenderer {
143+
144+ #text: Text ;
145+
146+ constructor ( config : NodeMessageConfig ) {
147+ this . #text = new Text ( config . message ,
148+ {
149+ color : MessageTypeColor ( config . type ? config . type : MessageType . Info ) ,
150+ } ,
151+ {
152+ LineSpacing : 2.5 ,
153+ MaxWidth : 200
154+ }
155+ ) ;
156+ }
157+
158+ render ( ctx : CanvasRenderingContext2D , scale : number , position : Vector2 ) : number {
159+ ctx . textAlign = TextAlign . Center ;
160+ this . #text. render ( ctx , scale , position ) ;
161+ return this . #text. height ( ctx ) * scale ;
162+ }
163+ }
164+
114165export class FlowNode {
115166
116167 #position: Vector2 ;
@@ -123,6 +174,8 @@ export class FlowNode {
123174
124175 #infoText: string ;
125176
177+ #messages: Array < MessageRenderer >
178+
126179 #input: Array < Port > ;
127180
128181 #output: Array < Port > ;
@@ -203,6 +256,14 @@ export class FlowNode {
203256 this . #contextMenu = config ?. contextMenu === undefined ? null : config . contextMenu ;
204257 this . #metadata = config ?. metadata ;
205258
259+ this . #messages = [ ] ;
260+ if ( config ?. messages ) {
261+ for ( let i = 0 ; i < config . messages . length ; i ++ ) {
262+ const message = config . messages [ i ] ;
263+ this . #messages. push ( new MessageRenderer ( message ) ) ;
264+ }
265+ }
266+
206267 this . #selected = false ;
207268 this . #onSelect = new Array < ( ) => void > ( ) ;
208269 this . #onUnselect = new Array < ( ) => void > ( ) ;
@@ -879,7 +940,6 @@ export class FlowNode {
879940 console . warn ( "setInfo instruction ignored, as node has been marked un-editable" ) ;
880941 }
881942
882-
883943 let cleaned = newInfo ;
884944 if ( cleaned === null || cleaned === undefined ) {
885945 cleaned = "" ;
@@ -935,6 +995,14 @@ export class FlowNode {
935995 return boxStyle ;
936996 }
937997
998+ addMessage ( message : NodeMessageConfig ) : void {
999+ this . #messages. push ( new MessageRenderer ( message ) ) ;
1000+ }
1001+
1002+ clearMessages ( ) : void {
1003+ this . #messages = [ ] ;
1004+ }
1005+
9381006 render ( ctx : CanvasRenderingContext2D , camera : Camera , state : NodeState , mousePosition : Vector2 | undefined , postProcess : PassSubsystem ) : void {
9391007 VectorPool . run ( ( ) => {
9401008 const tempMeasurement = VectorPool . get ( ) ;
@@ -1113,6 +1181,7 @@ export class FlowNode {
11131181 startY += tempMeasurement . y + scaledElementSpacing ;
11141182 }
11151183
1184+ // Widgets
11161185 for ( let i = 0 ; i < this . #widgets. length ; i ++ ) {
11171186 const widget = this . #widgets[ i ] ;
11181187 const widgetSize = widget . Size ( ) ;
@@ -1125,6 +1194,15 @@ export class FlowNode {
11251194 this . #widgetPositions. Push ( widget . Draw ( ctx , position , camera . zoom , mousePosition ) ) ;
11261195 startY += ( widgetSize . y * camera . zoom ) + scaledElementSpacing ;
11271196 }
1197+
1198+ // Messages
1199+ const messageStart = VectorPool . get ( ) ;
1200+ messageStart . x = nodeBounds . Position . x + ( nodeBounds . Size . x / 2 ) ;
1201+ messageStart . y = nodeBounds . Position . y + nodeBounds . Size . y + ( 15 * camera . zoom ) ;
1202+ for ( let i = 0 ; i < this . #messages. length ; i ++ ) {
1203+ const message = this . #messages[ i ] ;
1204+ messageStart . y += message . render ( ctx , camera . zoom , messageStart ) + ( 10 * camera . zoom ) ;
1205+ }
11281206 } )
11291207 }
11301208}
0 commit comments