1515using System . Reactive . Linq ;
1616using NodeDev . Core . Class ;
1717using NodeDev . Blazor . Services ;
18+ using NodeDev . Blazor . Services . GraphManager ;
1819
1920namespace NodeDev . Blazor . Components ;
2021
21- public partial class GraphCanvas : ComponentBase , IDisposable
22+ public partial class GraphCanvas : ComponentBase , IDisposable , IGraphCanvas
2223{
2324 [ Parameter , EditorRequired ]
2425 public Graph Graph { get ; set ; } = null ! ;
@@ -29,6 +30,9 @@ public partial class GraphCanvas : ComponentBase, IDisposable
2930 [ Inject ]
3031 internal DebuggedPathService DebuggedPathService { get ; set ; } = null ! ;
3132
33+ private GraphManagerService ? _GraphManagerService ;
34+ private GraphManagerService GraphManagerService => _GraphManagerService ??= new GraphManagerService ( this ) ;
35+
3236 private int PopupX = 0 ;
3337 private int PopupY = 0 ;
3438 private Vector2 PopupNodePosition ;
@@ -67,7 +71,6 @@ protected override void OnInitialized()
6771 Diagram . Links . Added += x => OnConnectionAdded ( x , false ) ;
6872 Diagram . Links . Removed += OnConnectionRemoved ;
6973 Diagram . SelectionChanged += SelectionChanged ;
70-
7174 }
7275
7376 #endregion
@@ -138,7 +141,7 @@ private void OnGraphChangedFromCore((Graph, bool) _)
138141
139142 #region UpdateConnectionType
140143
141- private void UpdateConnectionType ( Connection connection )
144+ public void UpdatePortColor ( Connection connection )
142145 {
143146 var node = Diagram . Nodes . OfType < GraphNodeModel > ( ) . FirstOrDefault ( x => x . Node == connection . Parent ) ;
144147 if ( node == null )
@@ -155,16 +158,6 @@ private void UpdateConnectionType(Connection connection)
155158
156159 #endregion
157160
158- #region UpdateNodeBaseInfo
159-
160- private void UpdateNodeBaseInfo ( Node node )
161- {
162- var nodeModel = Diagram . Nodes . OfType < GraphNodeModel > ( ) . First ( x => x . Node == node ) ;
163- nodeModel . UpdateNodeBaseInfo ( node ) ;
164- }
165-
166- #endregion
167-
168161 #region UpdateNodes
169162
170163 private void UpdateNodes ( IEnumerable < Node > nodes )
@@ -239,7 +232,7 @@ private void OnConnectionUpdated(BaseLinkModel baseLinkModel, Anchor old, Anchor
239232 {
240233 DisableConnectionUpdate = true ;
241234 var old = baseLinkModel . Source ;
242- baseLinkModel . SetSource ( baseLinkModel . Target ) ;
235+ baseLinkModel . SetSource ( baseLinkModel . Target ) ; // this is necessary as everything assumes that the source is an output and vice versa
243236 baseLinkModel . SetTarget ( old ) ;
244237 DisableConnectionUpdate = false ;
245238
@@ -248,47 +241,7 @@ private void OnConnectionUpdated(BaseLinkModel baseLinkModel, Anchor old, Anchor
248241 destination = tmp ;
249242 }
250243
251- Graph . Connect ( source . Connection , destination . Connection , false ) ;
252-
253- // we're plugging something something with a generic into something without a generic
254- if ( source . Connection . Type . HasUndefinedGenerics && ! destination . Connection . Type . HasUndefinedGenerics )
255- {
256- if ( source . Connection . Type . IsAssignableTo ( destination . Connection . Type , out var newTypes ) && newTypes . Count != 0 )
257- {
258- PropagateNewGeneric ( source . Connection . Parent , newTypes , false ) ;
259- }
260- }
261- else if ( destination . Connection . Type . HasUndefinedGenerics && ! source . Connection . Type . HasUndefinedGenerics )
262- {
263- if ( source . Connection . Type . IsAssignableTo ( destination . Connection . Type , out var newTypes ) && newTypes . Count != 0 )
264- {
265- PropagateNewGeneric ( destination . Connection . Parent , newTypes , false ) ;
266- }
267- }
268-
269- // we have to remove the textbox ?
270- if ( destination . Connection . Connections . Count == 1 && destination . Connection . Type . AllowTextboxEdit )
271- UpdateConnectionType ( destination . Connection ) ;
272-
273- if ( baseLinkModel is LinkModel link && link . Source . Model is GraphPortModel sourcePort && link . Target . Model is GraphPortModel targetPort )
274- {
275- link . Color = GetTypeShapeColor ( sourcePort . Connection . Type , sourcePort . Connection . Parent . TypeFactory ) ;
276- // we have to disconnect the previously connected exec, since exec outputs can only have one connection
277- if ( source . Connection . Type . IsExec && source . Connection . Connections . Count > 1 )
278- {
279- Diagram . Links . Remove ( Diagram . Links . First ( x => ( x . Source . Model as GraphPortModel ) ? . Connection == source . Connection && ( x . Target . Model as GraphPortModel ) ? . Connection != targetPort . Connection ) ) ;
280- Graph . Disconnect ( source . Connection , source . Connection . Connections . First ( x => x != targetPort . Connection ) , false ) ;
281- }
282- else if ( ! destination . Connection . Type . IsExec && destination . Connection . Connections . Count > 1 )
283- {
284- Diagram . Links . Remove ( Diagram . Links . First ( x => ( x . Source . Model as GraphPortModel ) ? . Connection != source . Connection && ( x . Target . Model as GraphPortModel ) ? . Connection == targetPort . Connection ) ) ;
285- Graph . Disconnect ( destination . Connection , destination . Connection . Connections . First ( x => x != sourcePort . Connection ) , false ) ;
286- }
287- }
288-
289- UpdateVerticesInConnection ( source . Connection , destination . Connection , baseLinkModel ) ;
290-
291- Graph . RaiseGraphChanged ( true ) ;
244+ GraphManagerService . AddNewConnectionBetween ( source . Connection , destination . Connection ) ;
292245 } ) ;
293246 }
294247
@@ -318,6 +271,11 @@ public void OnConnectionAdded(BaseLinkModel baseLinkModel, bool force)
318271 }
319272 }
320273
274+ /// <summary>
275+ /// Return the output connection except for execs, in that case we return the input connection.
276+ /// This is because vertices are stored for the port, and execs conveniently only have one output connection while other types only have one input connection.
277+ /// </summary>
278+ /// <returns></returns>
321279 private Connection GetConnectionContainingVertices ( Connection source , Connection destination )
322280 {
323281 if ( source . Type . IsExec ) // execs can only have one connection, therefor they always contains the vertex information
@@ -380,7 +338,7 @@ public void OnConnectionRemoved(BaseLinkModel baseLinkModel)
380338
381339 // We have to add back the textbox editor
382340 if ( destination . Connections . Count == 0 && destination . Type . AllowTextboxEdit )
383- UpdateConnectionType ( destination ) ;
341+ UpdatePortColor ( destination ) ;
384342
385343 UpdateVerticesInConnection ( source , destination , baseLinkModel ) ;
386344 }
@@ -433,11 +391,13 @@ private void OnNewNodeTypeSelected(NodeProvider.NodeSearchResult searchResult)
433391
434392 Diagram . Batch ( ( ) =>
435393 {
394+ CreateGraphNodeModel ( node ) ;
395+
436396 if ( PopupNodeConnection != null && PopupNode != null )
437397 {
438398 // check if the source was an input or output and choose the proper destination based on that
439399 List < Connection > sources , destinations ;
440- bool isPopupNodeInput = PopupNode . Inputs . Contains ( PopupNodeConnection ) ;
400+ bool isPopupNodeInput = PopupNodeConnection . IsInput ;
441401 if ( isPopupNodeInput )
442402 {
443403 sources = PopupNode . Inputs ;
@@ -453,43 +413,19 @@ private void OnNewNodeTypeSelected(NodeProvider.NodeSearchResult searchResult)
453413 if ( PopupNodeConnection . Type is UndefinedGenericType ) // can connect to anything except exec
454414 destination = destinations . FirstOrDefault ( x => ! x . Type . IsExec ) ;
455415 else // can connect to anything that is assignable to the type
456- destination = destinations . FirstOrDefault ( x => PopupNodeConnection . Type . IsAssignableTo ( x . Type , out _ ) || ( x . Type is UndefinedGenericType && ! PopupNodeConnection . Type . IsExec ) ) ;
416+ destination = destinations . FirstOrDefault ( x => PopupNodeConnection . Type . IsAssignableTo ( x . Type , out _ , out _ ) || ( x . Type is UndefinedGenericType && ! PopupNodeConnection . Type . IsExec ) ) ;
457417
458418 // if we found a connection, connect them together
459419 if ( destination != null )
460420 {
461- Graph . Connect ( PopupNodeConnection , destination , false ) ;
462-
463- if ( destination . Connections . Count == 1 && destination . Type . AllowTextboxEdit )
464- UpdateConnectionType ( destination ) ;
465- if ( PopupNodeConnection . Connections . Count == 1 && PopupNodeConnection . Type . AllowTextboxEdit )
466- UpdateConnectionType ( PopupNodeConnection ) ;
467-
468421 var source = isPopupNodeInput ? destination : PopupNodeConnection ;
469422 var target = isPopupNodeInput ? PopupNodeConnection : destination ;
470423
471- // check if we need to propagate some generic
472- if ( ! destination . Type . IsExec && source . Type . IsAssignableTo ( target . Type , out var changedGenerics ) )
473- {
474- PropagateNewGeneric ( node , changedGenerics , false ) ;
475- PropagateNewGeneric ( destination . Parent , changedGenerics , false ) ;
476- }
477- else if ( source . Type . IsExec && source . Connections . Count > 1 ) // check if we have to disconnect the previously connected exec
478- {
479- Diagram . Links . Remove ( Diagram . Links . First ( x => ( x . Source . Model as GraphPortModel ) ? . Connection == source && ( x . Target . Model as GraphPortModel ) ? . Connection != target ) ) ;
480- var toRemove = source . Connections . FirstOrDefault ( x => x != target ) ;
481- if ( toRemove != null )
482- Graph . Disconnect ( source , toRemove , false ) ;
483- }
424+ GraphManagerService . AddNewConnectionBetween ( source , target ) ;
484425 }
485426 }
486427
487428 CancelPopup ( ) ;
488-
489- CreateGraphNodeModel ( node ) ;
490- AddNodeLinks ( node , false ) ;
491-
492- UpdateNodes ( Graph . Nodes . Values . ToList ( ) ) ;
493429 } ) ;
494430
495431 }
@@ -513,9 +449,7 @@ private void OnNewOverloadSelected(Node.AlternateOverload overload)
513449 if ( PopupNode == null )
514450 return ;
515451
516- PopupNode . SelectOverload ( overload , out var newConnections , out var removedConnections ) ;
517-
518- Graph . MergedRemovedConnectionsWithNewConnections ( newConnections , removedConnections ) ;
452+ GraphManagerService . SelectNodeOverload ( PopupNode , overload ) ;
519453
520454 CancelPopup ( ) ;
521455 }
@@ -525,9 +459,9 @@ private void OnNewOverloadSelected(Node.AlternateOverload overload)
525459 #region OnGenericTypeSelectionMenuAsked
526460
527461 private bool IsShowingGenericTypeSelection = false ;
528- private UndefinedGenericType ? GenericTypeSelectionMenuGeneric ;
462+ private string ? GenericTypeSelectionMenuGeneric ;
529463
530- public void OnGenericTypeSelectionMenuAsked ( GraphNodeModel nodeModel , UndefinedGenericType undefinedGenericType )
464+ public void OnGenericTypeSelectionMenuAsked ( GraphNodeModel nodeModel , string undefinedGenericType )
531465 {
532466 PopupNode = nodeModel . Node ;
533467 var p = Diagram . GetScreenPoint ( nodeModel . Position . X , nodeModel . Position . Y ) - Diagram . Container ! . NorthWest ;
@@ -544,42 +478,14 @@ private void OnGenericTypeSelected(TypeBase type)
544478 if ( PopupNode == null || GenericTypeSelectionMenuGeneric == null )
545479 return ;
546480
547- PropagateNewGeneric ( PopupNode , new Dictionary < UndefinedGenericType , TypeBase > ( ) { [ GenericTypeSelectionMenuGeneric ] = type } , false ) ;
481+ GraphManagerService . PropagateNewGeneric ( PopupNode , new Dictionary < string , TypeBase > ( ) { [ GenericTypeSelectionMenuGeneric ] = type } , false , null , overrideInitialTypes : true ) ;
548482
549483 // Prefer updating the nodes directly instead of calling Graph.RaiseGraphChanged(true) to be sure it is called as soon as possible
550484 UpdateNodes ( Graph . Nodes . Values . ToList ( ) ) ;
551485
552486 CancelPopup ( ) ;
553487 }
554488
555- private void PropagateNewGeneric ( Node node , IReadOnlyDictionary < UndefinedGenericType , TypeBase > changedGenerics , bool requireUIRefresh )
556- {
557- foreach ( var port in node . InputsAndOutputs ) // check if any of the ports have the generic we just solved
558- {
559- if ( port . Type . GetUndefinedGenericTypes ( ) . Any ( changedGenerics . ContainsKey ) )
560- {
561- var isPortInput = node . Inputs . Contains ( port ) ;
562-
563- port . UpdateType ( port . Type . ReplaceUndefinedGeneric ( changedGenerics ) ) ;
564-
565- UpdateConnectionType ( port ) ;
566-
567- // check if other connections had their own generics and if we just solved them
568- foreach ( var other in port . Connections . ToList ( ) )
569- {
570- var source = isPortInput ? other : port ;
571- var target = isPortInput ? port : other ;
572- if ( source . Type . IsAssignableTo ( target . Type , out var changedGenerics2 ) && changedGenerics2 . Count != 0 )
573- PropagateNewGeneric ( other . Parent , changedGenerics2 , requireUIRefresh ) ;
574- else if ( ( changedGenerics2 ? . Count ?? 0 ) != 0 ) // damn, looks like changing the generic made it so we can't link to this connection anymore
575- Graph . Disconnect ( port , other , false ) ; // no need to refresh UI here as it'll already be refresh at the end of this method
576- }
577- }
578- }
579-
580- Graph . RaiseGraphChanged ( requireUIRefresh ) ;
581- }
582-
583489 #endregion
584490
585491 #region OnTextboxValueChanged
@@ -664,6 +570,26 @@ private void CancelPopup()
664570
665571 #endregion
666572
573+ #region RemoveLink
574+
575+ public void RemoveLinkFromGraphCanvas ( Connection source , Connection destination )
576+ {
577+ Graph . Invoke ( ( ) =>
578+ {
579+ DisableConnectionUpdate = true ;
580+ try
581+ {
582+ Diagram . Links . Remove ( Diagram . Links . First ( x => ( x . Source . Model as GraphPortModel ) ? . Connection == source && ( x . Target . Model as GraphPortModel ) ? . Connection == destination ) ) ;
583+ }
584+ finally
585+ {
586+ DisableConnectionUpdate = false ;
587+ }
588+ } ) ;
589+ }
590+
591+ #endregion
592+
667593 #region CreateGraphNodeModel
668594
669595 private void CreateGraphNodeModel ( Node node )
0 commit comments