private void DrawNodes() { Event e = Event.current; if (e.type == EventType.Layout) { selectionCache = new List <UnityEngine.Object>(Selection.objects); } System.Reflection.MethodInfo onValidate = null; if (Selection.activeObject != null && Selection.activeObject is XNode.Node) { onValidate = Selection.activeObject.GetType().GetMethod("OnValidate"); if (onValidate != null) { EditorGUI.BeginChangeCheck(); } } BeginZoomed(position, zoom, topPadding); Vector2 mousePos = Event.current.mousePosition; if (e.type != EventType.Layout) { hoveredNode = null; hoveredPort = null; } List <UnityEngine.Object> preSelection = preBoxSelection != null ? new List <UnityEngine.Object>(preBoxSelection) : new List <UnityEngine.Object>(); // Selection box stuff Vector2 boxStartPos = GridToWindowPositionNoClipped(dragBoxStart); Vector2 boxSize = mousePos - boxStartPos; if (boxSize.x < 0) { boxStartPos.x += boxSize.x; boxSize.x = Mathf.Abs(boxSize.x); } if (boxSize.y < 0) { boxStartPos.y += boxSize.y; boxSize.y = Mathf.Abs(boxSize.y); } Rect selectionBox = new Rect(boxStartPos, boxSize); //Save guiColor so we can revert it Color guiColor = GUI.color; List <XNode.NodePort> removeEntries = new List <XNode.NodePort>(); if (e.type == EventType.Layout) { culledNodes = new List <XNode.Node>(); } for (int n = 0; n < graph.nodes.Count; n++) { // Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable. if (graph.nodes[n] == null) { continue; } if (n >= graph.nodes.Count) { return; } XNode.Node node = graph.nodes[n]; // Culling if (e.type == EventType.Layout) { // Cull unselected nodes outside view if (!Selection.Contains(node) && ShouldBeCulled(node)) { culledNodes.Add(node); continue; } } else if (culledNodes.Contains(node)) { continue; } if (e.type == EventType.Repaint) { removeEntries.Clear(); foreach (var kvp in _portConnectionPoints) { if (kvp.Key.node == node) { removeEntries.Add(kvp.Key); } } foreach (var k in removeEntries) { _portConnectionPoints.Remove(k); } } NodeEditor nodeEditor = NodeEditor.GetEditor(node, this); NodeEditor.portPositions.Clear(); // Set default label width. This is potentially overridden in OnBodyGUI EditorGUIUtility.labelWidth = 84; //Get node position Vector2 nodePos = GridToWindowPositionNoClipped(node.position); GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000))); bool selected = selectionCache.Contains(graph.nodes[n]); if (selected) { GUIStyle style = new GUIStyle(nodeEditor.GetBodyStyle()); GUIStyle highlightStyle = new GUIStyle(nodeEditor.GetBodyHighlightStyle()); highlightStyle.padding = style.padding; style.padding = new RectOffset(); GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(style); GUI.color = NodeEditorPreferences.GetSettings().highlightColor; GUILayout.BeginVertical(new GUIStyle(highlightStyle)); } else { GUIStyle style = new GUIStyle(nodeEditor.GetBodyStyle()); GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(style); } GUI.color = guiColor; EditorGUI.BeginChangeCheck(); //Draw node contents nodeEditor.OnHeaderGUI(); //if (zoom < 2) //{ nodeEditor.OnBodyGUI(); //} //If user changed a value, notify other scripts through onUpdateNode if (EditorGUI.EndChangeCheck()) { if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(node); nodeEditor.serializedObject.ApplyModifiedProperties(); } GUILayout.EndVertical(); //Cache data about the node for next frame if (e.type == EventType.Repaint) { Vector2 size = GUILayoutUtility.GetLastRect().size; if (nodeSizes.ContainsKey(node)) { nodeSizes[node] = size; } else { nodeSizes.Add(node, size); } foreach (var kvp in NodeEditor.portPositions) { Vector2 portHandlePos = kvp.Value; portHandlePos += node.position; Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16); portConnectionPoints[kvp.Key] = rect; } } if (selected) { GUILayout.EndVertical(); } if (e.type != EventType.Layout) { //Check if we are hovering this node Vector2 nodeSize = GUILayoutUtility.GetLastRect().size; Rect windowRect = new Rect(nodePos, nodeSize); if (windowRect.Contains(mousePos)) { hoveredNode = node; } //If dragging a selection box, add nodes inside to selection if (currentActivity == NodeActivity.DragGrid) { if (windowRect.Overlaps(selectionBox)) { preSelection.Add(node); } } //Check if we are hovering any of this nodes ports //Check input ports foreach (XNode.NodePort input in node.Inputs) { //Check if port rect is available if (!portConnectionPoints.ContainsKey(input)) { continue; } Rect r = GridToWindowRectNoClipped(portConnectionPoints[input]); if (r.Contains(mousePos)) { hoveredPort = input; } } //Check all output ports foreach (XNode.NodePort output in node.Outputs) { //Check if port rect is available if (!portConnectionPoints.ContainsKey(output)) { continue; } Rect r = GridToWindowRectNoClipped(portConnectionPoints[output]); if (r.Contains(mousePos)) { hoveredPort = output; } } } GUILayout.EndArea(); } if (e.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) { Selection.objects = preSelection.ToArray(); } EndZoomed(position, zoom, topPadding); //If a change in is detected in the selected node, call OnValidate method. //This is done through reflection because OnValidate is only relevant in editor, //and thus, the code should not be included in build. if (onValidate != null && EditorGUI.EndChangeCheck()) { onValidate.Invoke(Selection.activeObject, null); } }