CreateNodeMenuWindow.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEngine;
  6. using UnityEditor.UIElements;
  7. using UnityEditor.Experimental.GraphView;
  8. using UnityEngine.UIElements;
  9. using UnityEditor;
  10. namespace GraphProcessor
  11. {
  12. // TODO: replace this by the new UnityEditor.Searcher package
  13. class CreateNodeMenuWindow : ScriptableObject, ISearchWindowProvider
  14. {
  15. BaseGraphView graphView;
  16. EditorWindow window;
  17. Texture2D icon;
  18. EdgeView edgeFilter;
  19. PortView inputPortView;
  20. PortView outputPortView;
  21. public void Initialize(BaseGraphView graphView, EditorWindow window, EdgeView edgeFilter = null)
  22. {
  23. this.graphView = graphView;
  24. this.window = window;
  25. this.edgeFilter = edgeFilter;
  26. this.inputPortView = edgeFilter?.input as PortView;
  27. this.outputPortView = edgeFilter?.output as PortView;
  28. // Transparent icon to trick search window into indenting items
  29. if (icon == null)
  30. icon = new Texture2D(1, 1);
  31. icon.SetPixel(0, 0, new Color(0, 0, 0, 0));
  32. icon.Apply();
  33. }
  34. void OnDestroy()
  35. {
  36. if (icon != null)
  37. {
  38. DestroyImmediate(icon);
  39. icon = null;
  40. }
  41. }
  42. public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
  43. {
  44. var tree = new List<SearchTreeEntry>
  45. {
  46. new SearchTreeGroupEntry(new GUIContent("Create Node"), 0),
  47. };
  48. if (edgeFilter == null)
  49. CreateStandardNodeMenu(tree);
  50. else
  51. CreateEdgeNodeMenu(tree);
  52. return tree;
  53. }
  54. void CreateStandardNodeMenu(List<SearchTreeEntry> tree)
  55. {
  56. // Sort menu by alphabetical order and submenus
  57. var nodeEntries = graphView.FilterCreateNodeMenuEntries().OrderBy(k => k.path);
  58. var titlePaths = new HashSet< string >();
  59. foreach (var nodeMenuItem in nodeEntries)
  60. {
  61. var nodePath = nodeMenuItem.path;
  62. var nodeName = nodePath;
  63. var level = 0;
  64. var parts = nodePath.Split('/');
  65. if(parts.Length > 1)
  66. {
  67. level++;
  68. nodeName = parts[parts.Length - 1];
  69. var fullTitleAsPath = "";
  70. for(var i = 0; i < parts.Length - 1; i++)
  71. {
  72. var title = parts[i];
  73. fullTitleAsPath += title;
  74. level = i + 1;
  75. // Add section title if the node is in subcategory
  76. if (!titlePaths.Contains(fullTitleAsPath))
  77. {
  78. tree.Add(new SearchTreeGroupEntry(new GUIContent(title)){
  79. level = level
  80. });
  81. titlePaths.Add(fullTitleAsPath);
  82. }
  83. }
  84. }
  85. tree.Add(new SearchTreeEntry(new GUIContent(nodeName, icon))
  86. {
  87. level = level + 1,
  88. userData = nodeMenuItem.type
  89. });
  90. }
  91. }
  92. void CreateEdgeNodeMenu(List<SearchTreeEntry> tree)
  93. {
  94. var entries = NodeProvider.GetEdgeCreationNodeMenuEntry((edgeFilter.input ?? edgeFilter.output) as PortView, graphView.graph);
  95. var titlePaths = new HashSet< string >();
  96. var nodePaths = NodeProvider.GetNodeMenuEntries(graphView.graph);
  97. tree.Add(new SearchTreeEntry(new GUIContent($"Relay", icon))
  98. {
  99. level = 1,
  100. userData = new NodeProvider.PortDescription{
  101. nodeType = typeof(RelayNode),
  102. portType = typeof(System.Object),
  103. isInput = inputPortView != null,
  104. portFieldName = inputPortView != null ? nameof(RelayNode.output) : nameof(RelayNode.input),
  105. portIdentifier = "0",
  106. portDisplayName = inputPortView != null ? "Out" : "In",
  107. }
  108. });
  109. var sortedMenuItems = entries.Select(port => (port, nodePaths.FirstOrDefault(kp => kp.type == port.nodeType).path)).OrderBy(e => e.path);
  110. // Sort menu by alphabetical order and submenus
  111. foreach (var nodeMenuItem in sortedMenuItems)
  112. {
  113. var nodePath = nodePaths.FirstOrDefault(kp => kp.type == nodeMenuItem.port.nodeType).path;
  114. // Ignore the node if it's not in the create menu
  115. if (String.IsNullOrEmpty(nodePath))
  116. continue;
  117. var nodeName = nodePath;
  118. var level = 0;
  119. var parts = nodePath.Split('/');
  120. if (parts.Length > 1)
  121. {
  122. level++;
  123. nodeName = parts[parts.Length - 1];
  124. var fullTitleAsPath = "";
  125. for (var i = 0; i < parts.Length - 1; i++)
  126. {
  127. var title = parts[i];
  128. fullTitleAsPath += title;
  129. level = i + 1;
  130. // Add section title if the node is in subcategory
  131. if (!titlePaths.Contains(fullTitleAsPath))
  132. {
  133. tree.Add(new SearchTreeGroupEntry(new GUIContent(title)){
  134. level = level
  135. });
  136. titlePaths.Add(fullTitleAsPath);
  137. }
  138. }
  139. }
  140. tree.Add(new SearchTreeEntry(new GUIContent($"{nodeName}: {nodeMenuItem.port.portDisplayName}", icon))
  141. {
  142. level = level + 1,
  143. userData = nodeMenuItem.port
  144. });
  145. }
  146. }
  147. // Node creation when validate a choice
  148. public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context)
  149. {
  150. // window to graph position
  151. var windowRoot = window.rootVisualElement;
  152. var windowMousePosition = windowRoot.ChangeCoordinatesTo(windowRoot.parent, context.screenMousePosition - window.position.position);
  153. var graphMousePosition = graphView.contentViewContainer.WorldToLocal(windowMousePosition);
  154. var nodeType = searchTreeEntry.userData is Type ? (Type)searchTreeEntry.userData : ((NodeProvider.PortDescription)searchTreeEntry.userData).nodeType;
  155. graphView.RegisterCompleteObjectUndo("Added " + nodeType);
  156. var view = graphView.AddNode(BaseNode.CreateFromType(nodeType, graphMousePosition));
  157. if (searchTreeEntry.userData is NodeProvider.PortDescription desc)
  158. {
  159. var targetPort = view.GetPortViewFromFieldName(desc.portFieldName, desc.portIdentifier);
  160. if (inputPortView == null)
  161. graphView.Connect(targetPort, outputPortView);
  162. else
  163. graphView.Connect(inputPortView, targetPort);
  164. }
  165. return true;
  166. }
  167. }
  168. }