BaseGraphView.cs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using UnityEditor.UIElements;
  6. using UnityEngine.UIElements;
  7. using UnityEditor.Experimental.GraphView;
  8. using System.Linq;
  9. using System;
  10. using UnityEditor.SceneManagement;
  11. using System.Reflection;
  12. using Status = UnityEngine.UIElements.DropdownMenuAction.Status;
  13. using Object = UnityEngine.Object;
  14. namespace GraphProcessor
  15. {
  16. /// <summary>
  17. /// Base class to write a custom view for a node
  18. /// </summary>
  19. public class BaseGraphView : GraphView, IDisposable
  20. {
  21. public delegate void ComputeOrderUpdatedDelegate();
  22. public delegate void NodeDuplicatedDelegate(BaseNode duplicatedNode, BaseNode newNode);
  23. /// <summary>
  24. /// Graph that owns of the node
  25. /// </summary>
  26. public BaseGraph graph;
  27. /// <summary>
  28. /// Connector listener that will create the edges between ports
  29. /// </summary>
  30. public BaseEdgeConnectorListener connectorListener;
  31. /// <summary>
  32. /// List of all node views in the graph
  33. /// </summary>
  34. /// <typeparam name="BaseNodeView"></typeparam>
  35. /// <returns></returns>
  36. public List< BaseNodeView > nodeViews = new List< BaseNodeView >();
  37. /// <summary>
  38. /// Dictionary of the node views accessed view the node instance, faster than a Find in the node view list
  39. /// </summary>
  40. /// <typeparam name="BaseNode"></typeparam>
  41. /// <typeparam name="BaseNodeView"></typeparam>
  42. /// <returns></returns>
  43. public Dictionary< BaseNode, BaseNodeView > nodeViewsPerNode = new Dictionary< BaseNode, BaseNodeView >();
  44. /// <summary>
  45. /// List of all edge views in the graph
  46. /// </summary>
  47. /// <typeparam name="EdgeView"></typeparam>
  48. /// <returns></returns>
  49. public List< EdgeView > edgeViews = new List< EdgeView >();
  50. /// <summary>
  51. /// List of all group views in the graph
  52. /// </summary>
  53. /// <typeparam name="GroupView"></typeparam>
  54. /// <returns></returns>
  55. public List< GroupView > groupViews = new List< GroupView >();
  56. #if UNITY_2020_1_OR_NEWER
  57. /// <summary>
  58. /// List of all sticky note views in the graph
  59. /// </summary>
  60. /// <typeparam name="StickyNoteView"></typeparam>
  61. /// <returns></returns>
  62. public List< StickyNoteView > stickyNoteViews = new List<StickyNoteView>();
  63. #endif
  64. /// <summary>
  65. /// List of all stack node views in the graph
  66. /// </summary>
  67. /// <typeparam name="BaseStackNodeView"></typeparam>
  68. /// <returns></returns>
  69. public List< BaseStackNodeView > stackNodeViews = new List< BaseStackNodeView >();
  70. Dictionary< Type, PinnedElementView > pinnedElements = new Dictionary< Type, PinnedElementView >();
  71. CreateNodeMenuWindow createNodeMenu;
  72. /// <summary>
  73. /// Triggered just after the graph is initialized
  74. /// </summary>
  75. public event Action initialized;
  76. /// <summary>
  77. /// Triggered just after the compute order of the graph is updated
  78. /// </summary>
  79. public event ComputeOrderUpdatedDelegate computeOrderUpdated;
  80. // Safe event relay from BaseGraph (safe because you are sure to always point on a valid BaseGraph
  81. // when one of these events is called), a graph switch can occur between two call tho
  82. /// <summary>
  83. /// Same event than BaseGraph.onExposedParameterListChanged
  84. /// Safe event (not triggered in case the graph is null).
  85. /// </summary>
  86. public event Action onExposedParameterListChanged;
  87. /// <summary>
  88. /// Same event than BaseGraph.onExposedParameterModified
  89. /// Safe event (not triggered in case the graph is null).
  90. /// </summary>
  91. public event Action< ExposedParameter > onExposedParameterModified;
  92. /// <summary>
  93. /// Triggered when a node is duplicated (crt-d) or copy-pasted (crtl-c/crtl-v)
  94. /// </summary>
  95. public event NodeDuplicatedDelegate nodeDuplicated;
  96. /// <summary>
  97. /// Object to handle nodes that shows their UI in the inspector.
  98. /// </summary>
  99. [SerializeField]
  100. protected NodeInspectorObject nodeInspector
  101. {
  102. get
  103. {
  104. if (graph.nodeInspectorReference == null)
  105. graph.nodeInspectorReference = CreateNodeInspectorObject();
  106. return graph.nodeInspectorReference as NodeInspectorObject;
  107. }
  108. }
  109. /// <summary>
  110. /// Workaround object for creating exposed parameter property fields.
  111. /// </summary>
  112. public ExposedParameterFieldFactory exposedParameterFactory { get; private set; }
  113. public SerializedObject serializedGraph { get; private set; }
  114. Dictionary<Type, (Type nodeType, MethodInfo initalizeNodeFromObject)> nodeTypePerCreateAssetType = new Dictionary<Type, (Type, MethodInfo)>();
  115. public BaseGraphView(EditorWindow window)
  116. {
  117. serializeGraphElements = SerializeGraphElementsCallback;
  118. canPasteSerializedData = CanPasteSerializedDataCallback;
  119. unserializeAndPaste = UnserializeAndPasteCallback;
  120. graphViewChanged = GraphViewChangedCallback;
  121. viewTransformChanged = ViewTransformChangedCallback;
  122. elementResized = ElementResizedCallback;
  123. RegisterCallback< KeyDownEvent >(KeyDownCallback);
  124. RegisterCallback< DragPerformEvent >(DragPerformedCallback);
  125. RegisterCallback< DragUpdatedEvent >(DragUpdatedCallback);
  126. RegisterCallback< MouseDownEvent >(MouseDownCallback);
  127. RegisterCallback< MouseUpEvent >(MouseUpCallback);
  128. InitializeManipulators();
  129. SetupZoom(0.05f, 2f);
  130. Undo.undoRedoPerformed += ReloadView;
  131. createNodeMenu = ScriptableObject.CreateInstance< CreateNodeMenuWindow >();
  132. createNodeMenu.Initialize(this, window);
  133. this.StretchToParentSize();
  134. }
  135. protected virtual NodeInspectorObject CreateNodeInspectorObject()
  136. {
  137. var inspector = ScriptableObject.CreateInstance<NodeInspectorObject>();
  138. inspector.name = "Node Inspector";
  139. inspector.hideFlags = HideFlags.HideAndDontSave ^ HideFlags.NotEditable;
  140. return inspector;
  141. }
  142. #region Callbacks
  143. protected override bool canCopySelection
  144. {
  145. get { return selection.Any(e => e is BaseNodeView || e is GroupView); }
  146. }
  147. protected override bool canCutSelection
  148. {
  149. get { return selection.Any(e => e is BaseNodeView || e is GroupView); }
  150. }
  151. string SerializeGraphElementsCallback(IEnumerable<GraphElement> elements)
  152. {
  153. var data = new CopyPasteHelper();
  154. foreach (BaseNodeView nodeView in elements.Where(e => e is BaseNodeView))
  155. {
  156. data.copiedNodes.Add(JsonSerializer.SerializeNode(nodeView.nodeTarget));
  157. foreach (var port in nodeView.nodeTarget.GetAllPorts())
  158. {
  159. if (port.portData.vertical)
  160. {
  161. foreach (var edge in port.GetEdges())
  162. data.copiedEdges.Add(JsonSerializer.Serialize(edge));
  163. }
  164. }
  165. }
  166. foreach (GroupView groupView in elements.Where(e => e is GroupView))
  167. data.copiedGroups.Add(JsonSerializer.Serialize(groupView.group));
  168. foreach (EdgeView edgeView in elements.Where(e => e is EdgeView))
  169. data.copiedEdges.Add(JsonSerializer.Serialize(edgeView.serializedEdge));
  170. ClearSelection();
  171. return JsonUtility.ToJson(data, true);
  172. }
  173. bool CanPasteSerializedDataCallback(string serializedData)
  174. {
  175. try {
  176. return JsonUtility.FromJson(serializedData, typeof(CopyPasteHelper)) != null;
  177. } catch {
  178. return false;
  179. }
  180. }
  181. void UnserializeAndPasteCallback(string operationName, string serializedData)
  182. {
  183. var data = JsonUtility.FromJson< CopyPasteHelper >(serializedData);
  184. RegisterCompleteObjectUndo(operationName);
  185. Dictionary<string, BaseNode> copiedNodesMap = new Dictionary<string, BaseNode>();
  186. var unserializedGroups = data.copiedGroups.Select(g => JsonSerializer.Deserialize<Group>(g)).ToList();
  187. foreach (var serializedNode in data.copiedNodes)
  188. {
  189. var node = JsonSerializer.DeserializeNode(serializedNode);
  190. if (node == null)
  191. continue ;
  192. string sourceGUID = node.GUID;
  193. graph.nodesPerGUID.TryGetValue(sourceGUID, out var sourceNode);
  194. //Call OnNodeCreated on the new fresh copied node
  195. node.createdFromDuplication = true;
  196. node.createdWithinGroup = unserializedGroups.Any(g => g.innerNodeGUIDs.Contains(sourceGUID));
  197. node.OnNodeCreated();
  198. //And move a bit the new node
  199. node.position.position += new Vector2(20, 20);
  200. var newNodeView = AddNode(node);
  201. // If the nodes were copied from another graph, then the source is null
  202. if (sourceNode != null)
  203. nodeDuplicated?.Invoke(sourceNode, node);
  204. copiedNodesMap[sourceGUID] = node;
  205. //Select the new node
  206. AddToSelection(nodeViewsPerNode[node]);
  207. }
  208. foreach (var group in unserializedGroups)
  209. {
  210. //Same than for node
  211. group.OnCreated();
  212. // try to centre the created node in the screen
  213. group.position.position += new Vector2(20, 20);
  214. var oldGUIDList = group.innerNodeGUIDs.ToList();
  215. group.innerNodeGUIDs.Clear();
  216. foreach (var guid in oldGUIDList)
  217. {
  218. graph.nodesPerGUID.TryGetValue(guid, out var node);
  219. // In case group was copied from another graph
  220. if (node == null)
  221. {
  222. copiedNodesMap.TryGetValue(guid, out node);
  223. group.innerNodeGUIDs.Add(node.GUID);
  224. }
  225. else
  226. {
  227. group.innerNodeGUIDs.Add(copiedNodesMap[guid].GUID);
  228. }
  229. }
  230. AddGroup(group);
  231. }
  232. foreach (var serializedEdge in data.copiedEdges)
  233. {
  234. var edge = JsonSerializer.Deserialize<SerializableEdge>(serializedEdge);
  235. edge.Deserialize();
  236. // Find port of new nodes:
  237. copiedNodesMap.TryGetValue(edge.inputNode.GUID, out var oldInputNode);
  238. copiedNodesMap.TryGetValue(edge.outputNode.GUID, out var oldOutputNode);
  239. // We avoid to break the graph by replacing unique connections:
  240. if (oldInputNode == null && !edge.inputPort.portData.acceptMultipleEdges || !edge.outputPort.portData.acceptMultipleEdges)
  241. continue;
  242. oldInputNode = oldInputNode ?? edge.inputNode;
  243. oldOutputNode = oldOutputNode ?? edge.outputNode;
  244. var inputPort = oldInputNode.GetPort(edge.inputPort.fieldName, edge.inputPortIdentifier);
  245. var outputPort = oldOutputNode.GetPort(edge.outputPort.fieldName, edge.outputPortIdentifier);
  246. var newEdge = SerializableEdge.CreateNewEdge(graph, inputPort, outputPort);
  247. if (nodeViewsPerNode.ContainsKey(oldInputNode) && nodeViewsPerNode.ContainsKey(oldOutputNode))
  248. {
  249. var edgeView = CreateEdgeView();
  250. edgeView.userData = newEdge;
  251. edgeView.input = nodeViewsPerNode[oldInputNode].GetPortViewFromFieldName(newEdge.inputFieldName, newEdge.inputPortIdentifier);
  252. edgeView.output = nodeViewsPerNode[oldOutputNode].GetPortViewFromFieldName(newEdge.outputFieldName, newEdge.outputPortIdentifier);
  253. Connect(edgeView);
  254. }
  255. }
  256. }
  257. public virtual EdgeView CreateEdgeView()
  258. {
  259. return new EdgeView();
  260. }
  261. GraphViewChange GraphViewChangedCallback(GraphViewChange changes)
  262. {
  263. if (changes.elementsToRemove != null)
  264. {
  265. RegisterCompleteObjectUndo("Remove Graph Elements");
  266. // Destroy priority of objects
  267. // We need nodes to be destroyed first because we can have a destroy operation that uses node connections
  268. changes.elementsToRemove.Sort((e1, e2) => {
  269. int GetPriority(GraphElement e)
  270. {
  271. if (e is BaseNodeView)
  272. return 0;
  273. else
  274. return 1;
  275. }
  276. return GetPriority(e1).CompareTo(GetPriority(e2));
  277. });
  278. //Handle ourselves the edge and node remove
  279. changes.elementsToRemove.RemoveAll(e => {
  280. switch (e)
  281. {
  282. case EdgeView edge:
  283. Disconnect(edge);
  284. return true;
  285. case BaseNodeView nodeView:
  286. // For vertical nodes, we need to delete them ourselves as it's not handled by GraphView
  287. foreach (var pv in nodeView.inputPortViews.Concat(nodeView.outputPortViews))
  288. if (pv.orientation == Orientation.Vertical)
  289. foreach (var edge in pv.GetEdges().ToList())
  290. Disconnect(edge);
  291. nodeInspector.NodeViewRemoved(nodeView);
  292. ExceptionToLog.Call(() => nodeView.OnRemoved());
  293. graph.RemoveNode(nodeView.nodeTarget);
  294. UpdateSerializedProperties();
  295. RemoveElement(nodeView);
  296. if (Selection.activeObject == nodeInspector)
  297. UpdateNodeInspectorSelection();
  298. SyncSerializedPropertyPathes();
  299. return true;
  300. case GroupView group:
  301. graph.RemoveGroup(group.group);
  302. UpdateSerializedProperties();
  303. RemoveElement(group);
  304. return true;
  305. case ExposedParameterFieldView blackboardField:
  306. graph.RemoveExposedParameter(blackboardField.parameter);
  307. UpdateSerializedProperties();
  308. return true;
  309. case BaseStackNodeView stackNodeView:
  310. graph.RemoveStackNode(stackNodeView.stackNode);
  311. UpdateSerializedProperties();
  312. RemoveElement(stackNodeView);
  313. return true;
  314. #if UNITY_2020_1_OR_NEWER
  315. case StickyNoteView stickyNoteView:
  316. graph.RemoveStickyNote(stickyNoteView.note);
  317. UpdateSerializedProperties();
  318. RemoveElement(stickyNoteView);
  319. return true;
  320. #endif
  321. }
  322. return false;
  323. });
  324. }
  325. return changes;
  326. }
  327. void GraphChangesCallback(GraphChanges changes)
  328. {
  329. if (changes.removedEdge != null)
  330. {
  331. var edge = edgeViews.FirstOrDefault(e => e.serializedEdge == changes.removedEdge);
  332. DisconnectView(edge);
  333. }
  334. }
  335. void ViewTransformChangedCallback(GraphView view)
  336. {
  337. if (graph != null)
  338. {
  339. graph.position = viewTransform.position;
  340. graph.scale = viewTransform.scale;
  341. }
  342. }
  343. void ElementResizedCallback(VisualElement elem)
  344. {
  345. var groupView = elem as GroupView;
  346. if (groupView != null)
  347. groupView.group.size = groupView.GetPosition().size;
  348. }
  349. public override List< Port > GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
  350. {
  351. var compatiblePorts = new List< Port >();
  352. compatiblePorts.AddRange(ports.ToList().Where(p => {
  353. var portView = p as PortView;
  354. if (portView.owner == (startPort as PortView).owner)
  355. return false;
  356. if (p.direction == startPort.direction)
  357. return false;
  358. //Check for type assignability
  359. if (!BaseGraph.TypesAreConnectable(startPort.portType, p.portType))
  360. return false;
  361. //Check if the edge already exists
  362. if (portView.GetEdges().Any(e => e.input == startPort || e.output == startPort))
  363. return false;
  364. return true;
  365. }));
  366. return compatiblePorts;
  367. }
  368. /// <summary>
  369. /// Build the contextual menu shown when right clicking inside the graph view
  370. /// </summary>
  371. /// <param name="evt"></param>
  372. public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
  373. {
  374. base.BuildContextualMenu(evt);
  375. BuildGroupContextualMenu(evt, 1);
  376. BuildStickyNoteContextualMenu(evt, 2);
  377. BuildViewContextualMenu(evt);
  378. BuildSelectAssetContextualMenu(evt);
  379. BuildSaveAssetContextualMenu(evt);
  380. BuildHelpContextualMenu(evt);
  381. }
  382. /// <summary>
  383. /// Add the New Group entry to the context menu
  384. /// </summary>
  385. /// <param name="evt"></param>
  386. protected virtual void BuildGroupContextualMenu(ContextualMenuPopulateEvent evt, int menuPosition = -1)
  387. {
  388. if (menuPosition == -1)
  389. menuPosition = evt.menu.MenuItems().Count;
  390. Vector2 position = (evt.currentTarget as VisualElement).ChangeCoordinatesTo(contentViewContainer, evt.localMousePosition);
  391. evt.menu.InsertAction(menuPosition, "Create Group", (e) => AddSelectionsToGroup(AddGroup(new Group("Create Group", position))), DropdownMenuAction.AlwaysEnabled);
  392. }
  393. /// <summary>
  394. /// -Add the New Sticky Note entry to the context menu
  395. /// </summary>
  396. /// <param name="evt"></param>
  397. protected virtual void BuildStickyNoteContextualMenu(ContextualMenuPopulateEvent evt, int menuPosition = -1)
  398. {
  399. if (menuPosition == -1)
  400. menuPosition = evt.menu.MenuItems().Count;
  401. #if UNITY_2020_1_OR_NEWER
  402. Vector2 position = (evt.currentTarget as VisualElement).ChangeCoordinatesTo(contentViewContainer, evt.localMousePosition);
  403. evt.menu.InsertAction(menuPosition, "Create Sticky Note", (e) => AddStickyNote(new StickyNote("Create Note", position)), DropdownMenuAction.AlwaysEnabled);
  404. #endif
  405. }
  406. /// <summary>
  407. /// Add the View entry to the context menu
  408. /// </summary>
  409. /// <param name="evt"></param>
  410. protected virtual void BuildViewContextualMenu(ContextualMenuPopulateEvent evt)
  411. {
  412. evt.menu.AppendAction("View/Processor", (e) => ToggleView< ProcessorView >(), (e) => GetPinnedElementStatus< ProcessorView >());
  413. }
  414. /// <summary>
  415. /// Add the Select Asset entry to the context menu
  416. /// </summary>
  417. /// <param name="evt"></param>
  418. protected virtual void BuildSelectAssetContextualMenu(ContextualMenuPopulateEvent evt)
  419. {
  420. evt.menu.AppendAction("Select Asset", (e) => EditorGUIUtility.PingObject(graph), DropdownMenuAction.AlwaysEnabled);
  421. }
  422. /// <summary>
  423. /// Add the Save Asset entry to the context menu
  424. /// </summary>
  425. /// <param name="evt"></param>
  426. protected virtual void BuildSaveAssetContextualMenu(ContextualMenuPopulateEvent evt)
  427. {
  428. evt.menu.AppendAction("Save Asset", (e) => {
  429. EditorUtility.SetDirty(graph);
  430. AssetDatabase.SaveAssets();
  431. }, DropdownMenuAction.AlwaysEnabled);
  432. }
  433. /// <summary>
  434. /// Add the Help entry to the context menu
  435. /// </summary>
  436. /// <param name="evt"></param>
  437. protected void BuildHelpContextualMenu(ContextualMenuPopulateEvent evt)
  438. {
  439. evt.menu.AppendAction("Help/Reset Pinned Windows", e => {
  440. foreach (var kp in pinnedElements)
  441. kp.Value.ResetPosition();
  442. });
  443. }
  444. protected virtual void KeyDownCallback(KeyDownEvent e)
  445. {
  446. if (e.keyCode == KeyCode.S && e.commandKey)
  447. {
  448. SaveGraphToDisk();
  449. e.StopPropagation();
  450. }
  451. else if(nodeViews.Count > 0 && e.commandKey && e.altKey)
  452. {
  453. // Node Aligning shortcuts
  454. switch(e.keyCode)
  455. {
  456. case KeyCode.LeftArrow:
  457. nodeViews[0].AlignToLeft();
  458. e.StopPropagation();
  459. break;
  460. case KeyCode.RightArrow:
  461. nodeViews[0].AlignToRight();
  462. e.StopPropagation();
  463. break;
  464. case KeyCode.UpArrow:
  465. nodeViews[0].AlignToTop();
  466. e.StopPropagation();
  467. break;
  468. case KeyCode.DownArrow:
  469. nodeViews[0].AlignToBottom();
  470. e.StopPropagation();
  471. break;
  472. case KeyCode.C:
  473. nodeViews[0].AlignToCenter();
  474. e.StopPropagation();
  475. break;
  476. case KeyCode.M:
  477. nodeViews[0].AlignToMiddle();
  478. e.StopPropagation();
  479. break;
  480. }
  481. }
  482. }
  483. void MouseUpCallback(MouseUpEvent e)
  484. {
  485. schedule.Execute(() => {
  486. if (DoesSelectionContainsInspectorNodes())
  487. UpdateNodeInspectorSelection();
  488. }).ExecuteLater(1);
  489. }
  490. void MouseDownCallback(MouseDownEvent e)
  491. {
  492. // When left clicking on the graph (not a node or something else)
  493. if (e.button == 0)
  494. {
  495. // Close all settings windows:
  496. nodeViews.ForEach(v => v.CloseSettings());
  497. }
  498. if (DoesSelectionContainsInspectorNodes())
  499. UpdateNodeInspectorSelection();
  500. }
  501. bool DoesSelectionContainsInspectorNodes()
  502. {
  503. var selectedNodes = selection.Where(s => s is BaseNodeView).ToList();
  504. var selectedNodesNotInInspector = selectedNodes.Except(nodeInspector.selectedNodes).ToList();
  505. var nodeInInspectorWithoutSelectedNodes = nodeInspector.selectedNodes.Except(selectedNodes).ToList();
  506. return selectedNodesNotInInspector.Any() || nodeInInspectorWithoutSelectedNodes.Any();
  507. }
  508. void DragPerformedCallback(DragPerformEvent e)
  509. {
  510. var mousePos = (e.currentTarget as VisualElement).ChangeCoordinatesTo(contentViewContainer, e.localMousePosition);
  511. var dragData = DragAndDrop.GetGenericData("DragSelection") as List< ISelectable >;
  512. // Drag and Drop for elements inside the graph
  513. if (dragData != null)
  514. {
  515. var exposedParameterFieldViews = dragData.OfType<ExposedParameterFieldView>();
  516. if (exposedParameterFieldViews.Any())
  517. {
  518. foreach (var paramFieldView in exposedParameterFieldViews)
  519. {
  520. RegisterCompleteObjectUndo("Create Parameter Node");
  521. var paramNode = BaseNode.CreateFromType< ParameterNode >(mousePos);
  522. paramNode.parameterGUID = paramFieldView.parameter.guid;
  523. AddNode(paramNode);
  524. }
  525. }
  526. }
  527. // External objects drag and drop
  528. if (DragAndDrop.objectReferences.Length > 0)
  529. {
  530. RegisterCompleteObjectUndo("Create Node From Object(s)");
  531. foreach (var obj in DragAndDrop.objectReferences)
  532. {
  533. var objectType = obj.GetType();
  534. foreach (var kp in nodeTypePerCreateAssetType)
  535. {
  536. if (kp.Key.IsAssignableFrom(objectType))
  537. {
  538. try
  539. {
  540. var node = BaseNode.CreateFromType(kp.Value.nodeType, mousePos);
  541. if ((bool)kp.Value.initalizeNodeFromObject.Invoke(node, new []{obj}))
  542. {
  543. AddNode(node);
  544. break;
  545. }
  546. }
  547. catch (Exception exception)
  548. {
  549. Debug.LogException(exception);
  550. }
  551. }
  552. }
  553. }
  554. }
  555. }
  556. void DragUpdatedCallback(DragUpdatedEvent e)
  557. {
  558. var dragData = DragAndDrop.GetGenericData("DragSelection") as List<ISelectable>;
  559. var dragObjects = DragAndDrop.objectReferences;
  560. bool dragging = false;
  561. if (dragData != null)
  562. {
  563. // Handle drag from exposed parameter view
  564. if (dragData.OfType<ExposedParameterFieldView>().Any())
  565. {
  566. dragging = true;
  567. }
  568. }
  569. if (dragObjects.Length > 0)
  570. dragging = true;
  571. if (dragging)
  572. DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
  573. UpdateNodeInspectorSelection();
  574. }
  575. #endregion
  576. #region Initialization
  577. void ReloadView()
  578. {
  579. // Force the graph to reload his data (Undo have updated the serialized properties of the graph
  580. // so the one that are not serialized need to be synchronized)
  581. graph.Deserialize();
  582. // Get selected nodes
  583. var selectedNodeGUIDs = new List<string>();
  584. foreach (var e in selection)
  585. {
  586. if (e is BaseNodeView v && this.Contains(v))
  587. selectedNodeGUIDs.Add(v.nodeTarget.GUID);
  588. }
  589. // Remove everything
  590. RemoveNodeViews();
  591. RemoveEdges();
  592. RemoveGroups();
  593. #if UNITY_2020_1_OR_NEWER
  594. RemoveStrickyNotes();
  595. #endif
  596. RemoveStackNodeViews();
  597. UpdateSerializedProperties();
  598. // And re-add with new up to date datas
  599. InitializeNodeViews();
  600. InitializeEdgeViews();
  601. InitializeGroups();
  602. InitializeStickyNotes();
  603. InitializeStackNodes();
  604. Reload();
  605. UpdateComputeOrder();
  606. // Restore selection after re-creating all views
  607. // selection = nodeViews.Where(v => selectedNodeGUIDs.Contains(v.nodeTarget.GUID)).Select(v => v as ISelectable).ToList();
  608. foreach (var guid in selectedNodeGUIDs)
  609. {
  610. AddToSelection(nodeViews.FirstOrDefault(n => n.nodeTarget.GUID == guid));
  611. }
  612. UpdateNodeInspectorSelection();
  613. }
  614. public void Initialize(BaseGraph graph)
  615. {
  616. if (this.graph != null)
  617. {
  618. SaveGraphToDisk();
  619. // Close pinned windows from old graph:
  620. ClearGraphElements();
  621. NodeProvider.UnloadGraph(graph);
  622. }
  623. this.graph = graph;
  624. exposedParameterFactory = new ExposedParameterFieldFactory(graph);
  625. UpdateSerializedProperties();
  626. connectorListener = CreateEdgeConnectorListener();
  627. // When pressing ctrl-s, we save the graph
  628. EditorSceneManager.sceneSaved += _ => SaveGraphToDisk();
  629. RegisterCallback<KeyDownEvent>(e => {
  630. if (e.keyCode == KeyCode.S && e.actionKey)
  631. SaveGraphToDisk();
  632. });
  633. ClearGraphElements();
  634. InitializeGraphView();
  635. InitializeNodeViews();
  636. InitializeEdgeViews();
  637. InitializeViews();
  638. InitializeGroups();
  639. InitializeStickyNotes();
  640. InitializeStackNodes();
  641. initialized?.Invoke();
  642. UpdateComputeOrder();
  643. InitializeView();
  644. NodeProvider.LoadGraph(graph);
  645. // Register the nodes that can be created from assets
  646. foreach (var nodeInfo in NodeProvider.GetNodeMenuEntries(graph))
  647. {
  648. var interfaces = nodeInfo.type.GetInterfaces();
  649. var exceptInheritedInterfaces = interfaces.Except(interfaces.SelectMany(t => t.GetInterfaces()));
  650. foreach (var i in interfaces)
  651. {
  652. if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICreateNodeFrom<>))
  653. {
  654. var genericArgumentType = i.GetGenericArguments()[0];
  655. var initializeFunction = nodeInfo.type.GetMethod(
  656. nameof(ICreateNodeFrom<Object>.InitializeNodeFromObject),
  657. BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
  658. null, new Type[]{ genericArgumentType}, null
  659. );
  660. // We only add the type that implements the interface, not it's children
  661. if (initializeFunction.DeclaringType == nodeInfo.type)
  662. nodeTypePerCreateAssetType[genericArgumentType] = (nodeInfo.type, initializeFunction);
  663. }
  664. }
  665. }
  666. }
  667. public void ClearGraphElements()
  668. {
  669. RemoveGroups();
  670. RemoveNodeViews();
  671. RemoveEdges();
  672. RemoveStackNodeViews();
  673. RemovePinnedElementViews();
  674. #if UNITY_2020_1_OR_NEWER
  675. RemoveStrickyNotes();
  676. #endif
  677. }
  678. void UpdateSerializedProperties()
  679. {
  680. serializedGraph = new SerializedObject(graph);
  681. }
  682. /// <summary>
  683. /// Allow you to create your own edge connector listener
  684. /// </summary>
  685. /// <returns></returns>
  686. protected virtual BaseEdgeConnectorListener CreateEdgeConnectorListener()
  687. => new BaseEdgeConnectorListener(this);
  688. void InitializeGraphView()
  689. {
  690. graph.onExposedParameterListChanged += OnExposedParameterListChanged;
  691. graph.onExposedParameterModified += (s) => onExposedParameterModified?.Invoke(s);
  692. graph.onGraphChanges += GraphChangesCallback;
  693. viewTransform.position = graph.position;
  694. viewTransform.scale = graph.scale;
  695. nodeCreationRequest = (c) => SearchWindow.Open(new SearchWindowContext(c.screenMousePosition), createNodeMenu);
  696. }
  697. void OnExposedParameterListChanged()
  698. {
  699. UpdateSerializedProperties();
  700. onExposedParameterListChanged?.Invoke();
  701. }
  702. void InitializeNodeViews()
  703. {
  704. graph.nodes.RemoveAll(n => n == null);
  705. foreach (var node in graph.nodes)
  706. {
  707. var v = AddNodeView(node);
  708. }
  709. }
  710. void InitializeEdgeViews()
  711. {
  712. // Sanitize edges in case a node broke something while loading
  713. graph.edges.RemoveAll(edge => edge == null || edge.inputNode == null || edge.outputNode == null);
  714. foreach (var serializedEdge in graph.edges)
  715. {
  716. nodeViewsPerNode.TryGetValue(serializedEdge.inputNode, out var inputNodeView);
  717. nodeViewsPerNode.TryGetValue(serializedEdge.outputNode, out var outputNodeView);
  718. if (inputNodeView == null || outputNodeView == null)
  719. continue;
  720. var edgeView = CreateEdgeView();
  721. edgeView.userData = serializedEdge;
  722. edgeView.input = inputNodeView.GetPortViewFromFieldName(serializedEdge.inputFieldName, serializedEdge.inputPortIdentifier);
  723. edgeView.output = outputNodeView.GetPortViewFromFieldName(serializedEdge.outputFieldName, serializedEdge.outputPortIdentifier);
  724. ConnectView(edgeView);
  725. }
  726. }
  727. void InitializeViews()
  728. {
  729. foreach (var pinnedElement in graph.pinnedElements)
  730. {
  731. if (pinnedElement.opened)
  732. OpenPinned(pinnedElement.editorType.type);
  733. }
  734. }
  735. void InitializeGroups()
  736. {
  737. foreach (var group in graph.groups)
  738. AddGroupView(group);
  739. }
  740. void InitializeStickyNotes()
  741. {
  742. #if UNITY_2020_1_OR_NEWER
  743. foreach (var group in graph.stickyNotes)
  744. AddStickyNoteView(group);
  745. #endif
  746. }
  747. void InitializeStackNodes()
  748. {
  749. foreach (var stackNode in graph.stackNodes)
  750. AddStackNodeView(stackNode);
  751. }
  752. protected virtual void InitializeManipulators()
  753. {
  754. this.AddManipulator(new ContentDragger());
  755. this.AddManipulator(new SelectionDragger());
  756. this.AddManipulator(new RectangleSelector());
  757. }
  758. protected virtual void Reload() {}
  759. #endregion
  760. #region Graph content modification
  761. public void UpdateNodeInspectorSelection()
  762. {
  763. if (nodeInspector.previouslySelectedObject != Selection.activeObject)
  764. nodeInspector.previouslySelectedObject = Selection.activeObject;
  765. HashSet<BaseNodeView> selectedNodeViews = new HashSet<BaseNodeView>();
  766. nodeInspector.selectedNodes.Clear();
  767. foreach (var e in selection)
  768. {
  769. if (e is BaseNodeView v && this.Contains(v) && v.nodeTarget.needsInspector)
  770. selectedNodeViews.Add(v);
  771. }
  772. nodeInspector.UpdateSelectedNodes(selectedNodeViews);
  773. if (Selection.activeObject != nodeInspector && selectedNodeViews.Count > 0)
  774. Selection.activeObject = nodeInspector;
  775. }
  776. public BaseNodeView AddNode(BaseNode node)
  777. {
  778. // This will initialize the node using the graph instance
  779. graph.AddNode(node);
  780. UpdateSerializedProperties();
  781. var view = AddNodeView(node);
  782. // Call create after the node have been initialized
  783. ExceptionToLog.Call(() => view.OnCreated());
  784. UpdateComputeOrder();
  785. return view;
  786. }
  787. public BaseNodeView AddNodeView(BaseNode node)
  788. {
  789. var viewType = NodeProvider.GetNodeViewTypeFromType(node.GetType());
  790. if (viewType == null)
  791. viewType = typeof(BaseNodeView);
  792. var baseNodeView = Activator.CreateInstance(viewType) as BaseNodeView;
  793. baseNodeView.Initialize(this, node);
  794. AddElement(baseNodeView);
  795. nodeViews.Add(baseNodeView);
  796. nodeViewsPerNode[node] = baseNodeView;
  797. return baseNodeView;
  798. }
  799. public void RemoveNode(BaseNode node)
  800. {
  801. var view = nodeViewsPerNode[node];
  802. RemoveNodeView(view);
  803. graph.RemoveNode(node);
  804. }
  805. public void RemoveNodeView(BaseNodeView nodeView)
  806. {
  807. RemoveElement(nodeView);
  808. nodeViews.Remove(nodeView);
  809. nodeViewsPerNode.Remove(nodeView.nodeTarget);
  810. }
  811. void RemoveNodeViews()
  812. {
  813. foreach (var nodeView in nodeViews)
  814. RemoveElement(nodeView);
  815. nodeViews.Clear();
  816. nodeViewsPerNode.Clear();
  817. }
  818. void RemoveStackNodeViews()
  819. {
  820. foreach (var stackView in stackNodeViews)
  821. RemoveElement(stackView);
  822. stackNodeViews.Clear();
  823. }
  824. void RemovePinnedElementViews()
  825. {
  826. foreach (var pinnedView in pinnedElements.Values)
  827. {
  828. if (Contains(pinnedView))
  829. Remove(pinnedView);
  830. }
  831. pinnedElements.Clear();
  832. }
  833. public GroupView AddGroup(Group block)
  834. {
  835. graph.AddGroup(block);
  836. block.OnCreated();
  837. return AddGroupView(block);
  838. }
  839. public GroupView AddGroupView(Group block)
  840. {
  841. var c = new GroupView();
  842. c.Initialize(this, block);
  843. AddElement(c);
  844. groupViews.Add(c);
  845. return c;
  846. }
  847. public BaseStackNodeView AddStackNode(BaseStackNode stackNode)
  848. {
  849. graph.AddStackNode(stackNode);
  850. return AddStackNodeView(stackNode);
  851. }
  852. public BaseStackNodeView AddStackNodeView(BaseStackNode stackNode)
  853. {
  854. var viewType = StackNodeViewProvider.GetStackNodeCustomViewType(stackNode.GetType()) ?? typeof(BaseStackNodeView);
  855. var stackView = Activator.CreateInstance(viewType, stackNode) as BaseStackNodeView;
  856. AddElement(stackView);
  857. stackNodeViews.Add(stackView);
  858. stackView.Initialize(this);
  859. return stackView;
  860. }
  861. public void RemoveStackNodeView(BaseStackNodeView stackNodeView)
  862. {
  863. stackNodeViews.Remove(stackNodeView);
  864. RemoveElement(stackNodeView);
  865. }
  866. #if UNITY_2020_1_OR_NEWER
  867. public StickyNoteView AddStickyNote(StickyNote note)
  868. {
  869. graph.AddStickyNote(note);
  870. return AddStickyNoteView(note);
  871. }
  872. public StickyNoteView AddStickyNoteView(StickyNote note)
  873. {
  874. var c = new StickyNoteView();
  875. c.Initialize(this, note);
  876. AddElement(c);
  877. stickyNoteViews.Add(c);
  878. return c;
  879. }
  880. public void RemoveStickyNoteView(StickyNoteView view)
  881. {
  882. stickyNoteViews.Remove(view);
  883. RemoveElement(view);
  884. }
  885. public void RemoveStrickyNotes()
  886. {
  887. foreach (var stickyNodeView in stickyNoteViews)
  888. RemoveElement(stickyNodeView);
  889. stickyNoteViews.Clear();
  890. }
  891. #endif
  892. public void AddSelectionsToGroup(GroupView view)
  893. {
  894. foreach (var selectedNode in selection)
  895. {
  896. if (selectedNode is BaseNodeView)
  897. {
  898. if (groupViews.Exists(x => x.ContainsElement(selectedNode as BaseNodeView)))
  899. continue;
  900. view.AddElement(selectedNode as BaseNodeView);
  901. }
  902. }
  903. }
  904. public void RemoveGroups()
  905. {
  906. foreach (var groupView in groupViews)
  907. RemoveElement(groupView);
  908. groupViews.Clear();
  909. }
  910. public bool CanConnectEdge(EdgeView e, bool autoDisconnectInputs = true)
  911. {
  912. if (e.input == null || e.output == null)
  913. return false;
  914. var inputPortView = e.input as PortView;
  915. var outputPortView = e.output as PortView;
  916. var inputNodeView = inputPortView.node as BaseNodeView;
  917. var outputNodeView = outputPortView.node as BaseNodeView;
  918. if (inputNodeView == null || outputNodeView == null)
  919. {
  920. Debug.LogError("Connect aborted !");
  921. return false;
  922. }
  923. return true;
  924. }
  925. public bool ConnectView(EdgeView e, bool autoDisconnectInputs = true)
  926. {
  927. if (!CanConnectEdge(e, autoDisconnectInputs))
  928. return false;
  929. var inputPortView = e.input as PortView;
  930. var outputPortView = e.output as PortView;
  931. var inputNodeView = inputPortView.node as BaseNodeView;
  932. var outputNodeView = outputPortView.node as BaseNodeView;
  933. //If the input port does not support multi-connection, we remove them
  934. if (autoDisconnectInputs && !(e.input as PortView).portData.acceptMultipleEdges)
  935. {
  936. foreach (var edge in edgeViews.Where(ev => ev.input == e.input).ToList())
  937. {
  938. // TODO: do not disconnect them if the connected port is the same than the old connected
  939. DisconnectView(edge);
  940. }
  941. }
  942. // same for the output port:
  943. if (autoDisconnectInputs && !(e.output as PortView).portData.acceptMultipleEdges)
  944. {
  945. foreach (var edge in edgeViews.Where(ev => ev.output == e.output).ToList())
  946. {
  947. // TODO: do not disconnect them if the connected port is the same than the old connected
  948. DisconnectView(edge);
  949. }
  950. }
  951. AddElement(e);
  952. e.input.Connect(e);
  953. e.output.Connect(e);
  954. // If the input port have been removed by the custom port behavior
  955. // we try to find if it's still here
  956. if (e.input == null)
  957. e.input = inputNodeView.GetPortViewFromFieldName(inputPortView.fieldName, inputPortView.portData.identifier);
  958. if (e.output == null)
  959. e.output = inputNodeView.GetPortViewFromFieldName(outputPortView.fieldName, outputPortView.portData.identifier);
  960. edgeViews.Add(e);
  961. inputNodeView.RefreshPorts();
  962. outputNodeView.RefreshPorts();
  963. // In certain cases the edge color is wrong so we patch it
  964. schedule.Execute(() => {
  965. e.UpdateEdgeControl();
  966. }).ExecuteLater(1);
  967. e.isConnected = true;
  968. return true;
  969. }
  970. public bool Connect(PortView inputPortView, PortView outputPortView, bool autoDisconnectInputs = true)
  971. {
  972. var inputPort = inputPortView.owner.nodeTarget.GetPort(inputPortView.fieldName, inputPortView.portData.identifier);
  973. var outputPort = outputPortView.owner.nodeTarget.GetPort(outputPortView.fieldName, outputPortView.portData.identifier);
  974. // Checks that the node we are connecting still exists
  975. if (inputPortView.owner.parent == null || outputPortView.owner.parent == null)
  976. return false;
  977. var newEdge = SerializableEdge.CreateNewEdge(graph, inputPort, outputPort);
  978. var edgeView = CreateEdgeView();
  979. edgeView.userData = newEdge;
  980. edgeView.input = inputPortView;
  981. edgeView.output = outputPortView;
  982. return Connect(edgeView);
  983. }
  984. public bool Connect(EdgeView e, bool autoDisconnectInputs = true)
  985. {
  986. if (!CanConnectEdge(e, autoDisconnectInputs))
  987. return false;
  988. var inputPortView = e.input as PortView;
  989. var outputPortView = e.output as PortView;
  990. var inputNodeView = inputPortView.node as BaseNodeView;
  991. var outputNodeView = outputPortView.node as BaseNodeView;
  992. var inputPort = inputNodeView.nodeTarget.GetPort(inputPortView.fieldName, inputPortView.portData.identifier);
  993. var outputPort = outputNodeView.nodeTarget.GetPort(outputPortView.fieldName, outputPortView.portData.identifier);
  994. e.userData = graph.Connect(inputPort, outputPort, autoDisconnectInputs);
  995. ConnectView(e, autoDisconnectInputs);
  996. UpdateComputeOrder();
  997. return true;
  998. }
  999. public void DisconnectView(EdgeView e, bool refreshPorts = true)
  1000. {
  1001. if (e == null)
  1002. return ;
  1003. RemoveElement(e);
  1004. if (e?.input?.node is BaseNodeView inputNodeView)
  1005. {
  1006. e.input.Disconnect(e);
  1007. if (refreshPorts)
  1008. inputNodeView.RefreshPorts();
  1009. }
  1010. if (e?.output?.node is BaseNodeView outputNodeView)
  1011. {
  1012. e.output.Disconnect(e);
  1013. if (refreshPorts)
  1014. outputNodeView.RefreshPorts();
  1015. }
  1016. edgeViews.Remove(e);
  1017. }
  1018. public void Disconnect(EdgeView e, bool refreshPorts = true)
  1019. {
  1020. // Remove the serialized edge if there is one
  1021. if (e.userData is SerializableEdge serializableEdge)
  1022. graph.Disconnect(serializableEdge.GUID);
  1023. DisconnectView(e, refreshPorts);
  1024. UpdateComputeOrder();
  1025. }
  1026. public void RemoveEdges()
  1027. {
  1028. foreach (var edge in edgeViews)
  1029. RemoveElement(edge);
  1030. edgeViews.Clear();
  1031. }
  1032. public void UpdateComputeOrder()
  1033. {
  1034. graph.UpdateComputeOrder();
  1035. computeOrderUpdated?.Invoke();
  1036. }
  1037. public void RegisterCompleteObjectUndo(string name)
  1038. {
  1039. Undo.RegisterCompleteObjectUndo(graph, name);
  1040. }
  1041. public void SaveGraphToDisk()
  1042. {
  1043. if (graph == null)
  1044. return ;
  1045. EditorUtility.SetDirty(graph);
  1046. }
  1047. public void ToggleView< T >() where T : PinnedElementView
  1048. {
  1049. ToggleView(typeof(T));
  1050. }
  1051. public void ToggleView(Type type)
  1052. {
  1053. PinnedElementView view;
  1054. pinnedElements.TryGetValue(type, out view);
  1055. if (view == null)
  1056. OpenPinned(type);
  1057. else
  1058. ClosePinned(type, view);
  1059. }
  1060. public void OpenPinned< T >() where T : PinnedElementView
  1061. {
  1062. OpenPinned(typeof(T));
  1063. }
  1064. public void OpenPinned(Type type)
  1065. {
  1066. PinnedElementView view;
  1067. if (type == null)
  1068. return ;
  1069. PinnedElement elem = graph.OpenPinned(type);
  1070. if (!pinnedElements.ContainsKey(type))
  1071. {
  1072. view = Activator.CreateInstance(type) as PinnedElementView;
  1073. if (view == null)
  1074. return ;
  1075. pinnedElements[type] = view;
  1076. view.InitializeGraphView(elem, this);
  1077. }
  1078. view = pinnedElements[type];
  1079. if (!Contains(view))
  1080. Add(view);
  1081. }
  1082. public void ClosePinned< T >(PinnedElementView view) where T : PinnedElementView
  1083. {
  1084. ClosePinned(typeof(T), view);
  1085. }
  1086. public void ClosePinned(Type type, PinnedElementView elem)
  1087. {
  1088. pinnedElements.Remove(type);
  1089. Remove(elem);
  1090. graph.ClosePinned(type);
  1091. }
  1092. public Status GetPinnedElementStatus< T >() where T : PinnedElementView
  1093. {
  1094. return GetPinnedElementStatus(typeof(T));
  1095. }
  1096. public Status GetPinnedElementStatus(Type type)
  1097. {
  1098. var pinned = graph.pinnedElements.Find(p => p.editorType.type == type);
  1099. if (pinned != null && pinned.opened)
  1100. return Status.Normal;
  1101. else
  1102. return Status.Hidden;
  1103. }
  1104. public void ResetPositionAndZoom()
  1105. {
  1106. graph.position = Vector3.zero;
  1107. graph.scale = Vector3.one;
  1108. UpdateViewTransform(graph.position, graph.scale);
  1109. }
  1110. /// <summary>
  1111. /// Deletes the selected content, can be called form an IMGUI container
  1112. /// </summary>
  1113. public void DelayedDeleteSelection() => this.schedule.Execute(() => DeleteSelectionOperation("Delete", AskUser.DontAskUser)).ExecuteLater(0);
  1114. protected virtual void InitializeView() {}
  1115. public virtual IEnumerable<(string path, Type type)> FilterCreateNodeMenuEntries()
  1116. {
  1117. // By default we don't filter anything
  1118. foreach (var nodeMenuItem in NodeProvider.GetNodeMenuEntries(graph))
  1119. yield return nodeMenuItem;
  1120. // TODO: add exposed properties to this list
  1121. }
  1122. public RelayNodeView AddRelayNode(PortView inputPort, PortView outputPort, Vector2 position)
  1123. {
  1124. var relayNode = BaseNode.CreateFromType<RelayNode>(position);
  1125. var view = AddNode(relayNode) as RelayNodeView;
  1126. if (outputPort != null)
  1127. Connect(view.inputPortViews[0], outputPort);
  1128. if (inputPort != null)
  1129. Connect(inputPort, view.outputPortViews[0]);
  1130. return view;
  1131. }
  1132. /// <summary>
  1133. /// Update all the serialized property bindings (in case a node was deleted / added, the property pathes needs to be updated)
  1134. /// </summary>
  1135. public void SyncSerializedPropertyPathes()
  1136. {
  1137. foreach (var nodeView in nodeViews)
  1138. nodeView.SyncSerializedPropertyPathes();
  1139. nodeInspector.RefreshNodes();
  1140. }
  1141. /// <summary>
  1142. /// Call this function when you want to remove this view
  1143. /// </summary>
  1144. public void Dispose()
  1145. {
  1146. ClearGraphElements();
  1147. RemoveFromHierarchy();
  1148. Undo.undoRedoPerformed -= ReloadView;
  1149. Object.DestroyImmediate(nodeInspector);
  1150. NodeProvider.UnloadGraph(graph);
  1151. exposedParameterFactory.Dispose();
  1152. exposedParameterFactory = null;
  1153. graph.onExposedParameterListChanged -= OnExposedParameterListChanged;
  1154. graph.onExposedParameterModified += (s) => onExposedParameterModified?.Invoke(s);
  1155. graph.onGraphChanges -= GraphChangesCallback;
  1156. }
  1157. #endregion
  1158. }
  1159. }