private void OnGUI() { if (firstFrame) { UpdatePositionToMouse(); firstFrame = false; } GUI.SetNextControlName(inputControlName); input = EditorGUILayout.TextField(input); EditorGUI.FocusTextInControl(inputControlName); Event e = Event.current; // If input is empty, revert name to default instead if (input == null || input.Trim() == "") { if (GUILayout.Button("Revert to default") || (e.isKey && e.keyCode == KeyCode.Return)) { target.name = NodeEditorUtilities.NodeDefaultName(target.GetType()); NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename(); AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target)); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); target.TriggerOnValidate(); } } // Rename asset to input text else { if (GUILayout.Button("Apply") || (e.isKey && e.keyCode == KeyCode.Return)) { target.name = input; NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename(); AssetDatabase.SetMainObject((target as XNode.Node).graph, AssetDatabase.GetAssetPath(target)); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); target.TriggerOnValidate(); } } if (e.isKey && e.keyCode == KeyCode.Escape) { Close(); } }
/// <summary> Make a simple port field. </summary> public static void PortField(Vector2 position, XNode.NodePort port) { if (port == null) { return; } Rect rect = new Rect(position, new Vector2(16, 16)); NodeEditor editor = NodeEditor.GetEditor(port.node, NodeEditorWindow.current); Color backgroundColor = editor.GetTint(); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); // Register the handle position Vector2 portPos = rect.center; NodeEditor.portPositions[port] = portPos; }
/// <summary> Show right-click context menu for selected nodes </summary> public void ShowNodeContextMenu() { GenericMenu contextMenu = new GenericMenu(); // If only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; contextMenu.AddItem(new GUIContent("Move To Top"), false, () => MoveNodeToTop(node)); contextMenu.AddItem(new GUIContent("Rename"), false, NodeEditor.GetEditor(node).InitiateRename); } contextMenu.AddItem(new GUIContent("Duplicate"), false, DublicateSelectedNodes); contextMenu.AddItem(new GUIContent("Remove"), false, RemoveSelectedNodes); // If only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; AddCustomContextMenuItems(contextMenu, node); } contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); }
public void Controls() { wantsMouseMove = true; Event e = Event.current; switch (e.type) { case EventType.DragUpdated: case EventType.DragPerform: DragAndDrop.visualMode = DragAndDropVisualMode.Generic; if (e.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); graphEditor.OnDropObjects(DragAndDrop.objectReferences); } break; case EventType.MouseMove: //Keyboard commands will not get correct mouse position from Event lastMousePosition = e.mousePosition; break; case EventType.ScrollWheel: float oldZoom = zoom; if (e.delta.y > 0) { zoom += 0.1f * zoom; } else { zoom -= 0.1f * zoom; } if (NodeEditorPreferences.GetSettings().zoomToMouse) { panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset); } break; case EventType.MouseDrag: if (e.button == 0) { if (IsDraggingPort) { if (IsHoveringPort && hoveredPort.IsInput && draggedOutput.CanConnectTo(hoveredPort)) { if (!draggedOutput.IsConnectedTo(hoveredPort)) { draggedOutputTarget = hoveredPort; } } else { draggedOutputTarget = null; } Repaint(); } else if (currentActivity == NodeActivity.HoldNode) { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); } if (currentActivity == NodeActivity.DragNode) { // Holding ctrl inverts grid snap bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap; if (e.control) { gridSnap = !gridSnap; } Vector2 mousePos = WindowToGridPosition(e.mousePosition); // Move selected nodes with offset for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; Vector2 initial = node.position; node.position = mousePos + dragOffset[i]; if (gridSnap) { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame. Vector2 offset = node.position - initial; if (offset.sqrMagnitude > 0) { foreach (XNode.NodePort output in node.Outputs) { Rect rect; if (portConnectionPoints.TryGetValue(output, out rect)) { rect.position += offset; portConnectionPoints[output] = rect; } } foreach (XNode.NodePort input in node.Inputs) { Rect rect; if (portConnectionPoints.TryGetValue(input, out rect)) { rect.position += offset; portConnectionPoints[input] = rect; } } } } } // Move selected reroutes with offset for (int i = 0; i < selectedReroutes.Count; i++) { Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i]; if (gridSnap) { pos.x = (Mathf.Round(pos.x / 16) * 16); pos.y = (Mathf.Round(pos.y / 16) * 16); } selectedReroutes[i].SetPoint(pos); } Repaint(); } else if (currentActivity == NodeActivity.HoldGrid) { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; preBoxSelectionReroute = selectedReroutes.ToArray(); dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); } else if (currentActivity == NodeActivity.DragGrid) { Vector2 boxStartPos = GridToWindowPosition(dragBoxStart); Vector2 boxSize = e.mousePosition - 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); } selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } } else if (e.button == 1 || e.button == 2) { panOffset += e.delta * zoom; isPanning = true; } break; case EventType.MouseDown: Repaint(); if (e.button == 0) { draggedOutputReroutes.Clear(); if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; autoConnectOutput = hoveredPort; } else { hoveredPort.VerifyConnections(); autoConnectOutput = null; if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex); hoveredPort.Disconnect(output); draggedOutput = output; draggedOutputTarget = hoveredPort; if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { // If mousedown on node header, select or deselect if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, e.control || e.shift); if (!e.control && !e.shift) { selectedReroutes.Clear(); } } else if (e.control || e.shift) { DeselectNode(hoveredNode); } // Cache double click state, but only act on it in MouseUp - Except ClickCount only works in mouseDown. isDoubleClick = (e.clickCount == 2); e.Use(); currentActivity = NodeActivity.HoldNode; } else if (IsHoveringReroute) { // If reroute isn't selected if (!selectedReroutes.Contains(hoveredReroute)) { // Add it if (e.control || e.shift) { selectedReroutes.Add(hoveredReroute); } // Select it else { selectedReroutes = new List <RerouteReference>() { hoveredReroute }; Selection.activeObject = null; } } // Deselect else if (e.control || e.shift) { selectedReroutes.Remove(hoveredReroute); } e.Use(); currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all else if (!IsHoveringNode) { currentActivity = NodeActivity.HoldGrid; if (!e.control && !e.shift) { selectedReroutes.Clear(); Selection.activeObject = null; } } } break; case EventType.MouseUp: if (e.button == 0) { //Port drag release if (IsDraggingPort) { //If connection is valid, save it if (draggedOutputTarget != null) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) { draggedOutput.Connect(draggedOutputTarget); } // ConnectionIndex can be -1 if the connection is removed instantly after creation int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget); if (connectionIndex != -1) { draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(graph); } } // Open context menu for auto-connection else if (NodeEditorPreferences.GetSettings().dragToCreate&& autoConnectOutput != null) { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } //Release dragged connection draggedOutput = null; draggedOutputTarget = null; EditorUtility.SetDirty(graph); if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } else if (currentActivity == NodeActivity.DragNode) { IEnumerable <XNode.Node> nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node); foreach (XNode.Node node in nodes) { EditorUtility.SetDirty(node); } if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } else if (!IsHoveringNode) { // If click outside node, release field focus if (!isPanning) { EditorGUI.FocusTextInControl(null); EditorGUIUtility.editingTextField = false; } if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } // If click node header, select it. if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { selectedReroutes.Clear(); SelectNode(hoveredNode, false); // Double click to center node if (isDoubleClick) { Vector2 nodeDimension = nodeSizes.ContainsKey(hoveredNode) ? nodeSizes[hoveredNode] / 2 : Vector2.zero; panOffset = -hoveredNode.position - nodeDimension; } } // If click reroute, select it. if (IsHoveringReroute && !(e.control || e.shift)) { selectedReroutes = new List <RerouteReference>() { hoveredReroute }; Selection.activeObject = null; } Repaint(); currentActivity = NodeActivity.Idle; } else if (e.button == 1 || e.button == 2) { if (!isPanning) { if (IsDraggingPort) { draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition)); } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) { selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint()); selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1); } else if (IsHoveringReroute) { ShowRerouteContextMenu(hoveredReroute); } else if (IsHoveringPort) { ShowPortContextMenu(hoveredPort); } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, false); } autoConnectOutput = null; GenericMenu menu = new GenericMenu(); NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places. } else if (!IsHoveringNode) { autoConnectOutput = null; GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } } isPanning = false; } // Reset DoubleClick isDoubleClick = false; break; case EventType.KeyDown: if (EditorGUIUtility.editingTextField) { break; } else if (e.keyCode == KeyCode.F) { Home(); } if (NodeEditorUtilities.IsMac()) { if (e.keyCode == KeyCode.Return) { RenameSelectedNode(); } } else { if (e.keyCode == KeyCode.F2) { RenameSelectedNode(); } } if (e.keyCode == KeyCode.A) { if (Selection.objects.Any(x => graph.nodes.Contains(x as XNode.Node))) { foreach (XNode.Node node in graph.nodes) { DeselectNode(node); } } else { foreach (XNode.Node node in graph.nodes) { SelectNode(node, true); } } Repaint(); } break; case EventType.ValidateCommand: case EventType.ExecuteCommand: if (e.commandName == "SoftDelete") { if (e.type == EventType.ExecuteCommand) { RemoveSelectedNodes(); } e.Use(); } else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") { if (e.type == EventType.ExecuteCommand) { RemoveSelectedNodes(); } e.Use(); } else if (e.commandName == "Duplicate") { if (e.type == EventType.ExecuteCommand) { DuplicateSelectedNodes(); } e.Use(); } else if (e.commandName == "Copy") { if (e.type == EventType.ExecuteCommand) { CopySelectedNodes(); } e.Use(); } else if (e.commandName == "Paste") { if (e.type == EventType.ExecuteCommand) { PasteNodes(WindowToGridPosition(lastMousePosition)); } e.Use(); } Repaint(); break; case EventType.Ignore: // If release mouse outside window if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { Repaint(); currentActivity = NodeActivity.Idle; } break; } }
private void DrawNodes() { Event e = Event.current; if (e.type == EventType.Layout) { selectionCache = new List <UnityEngine.Object>(Selection.objects); } if (e.type == EventType.Repaint) { portConnectionPoints.Clear(); nodeWidths.Clear(); } //Active node is hashed before and after node GUI to detect changes int nodeHash = 0; System.Reflection.MethodInfo onValidate = null; if (Selection.activeObject != null && Selection.activeObject is Siccity.XNode.Node) { onValidate = Selection.activeObject.GetType().GetMethod("OnValidate"); if (onValidate != null) { nodeHash = Selection.activeObject.GetHashCode(); } } BeginZoomed(position, zoom); 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>(); //Save guiColor so we can revert it Color guiColor = GUI.color; 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; } Siccity.XNode.Node node = graph.nodes[n]; NodeEditor nodeEditor = NodeEditor.GetEditor(node); NodeEditor.portPositions = new Dictionary <Siccity.XNode.NodePort, Vector2>(); //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(NodeEditorResources.styles.nodeBody); GUIStyle highlightStyle = new GUIStyle(NodeEditorResources.styles.nodeHighlight); highlightStyle.padding = style.padding; style.padding = new RectOffset(); GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(new GUIStyle(style)); GUI.color = NodeEditorPreferences.GetSettings().highlightColor; GUILayout.BeginVertical(new GUIStyle(highlightStyle)); } else { GUIStyle style = NodeEditorResources.styles.nodeBody; GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(new GUIStyle(style)); } GUI.color = guiColor; EditorGUI.BeginChangeCheck(); //Draw node contents nodeEditor.OnNodeGUI(); //Apply nodeEditor.serializedObject.ApplyModifiedProperties(); //If user changed a value, notify other scripts through onUpdateNode if (EditorGUI.EndChangeCheck()) { if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } if (e.type == EventType.Repaint) { nodeWidths.Add(node, nodeEditor.GetWidth()); 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.Add(kvp.Key, rect); } } GUILayout.EndVertical(); 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) { Vector2 startPos = GridToWindowPositionNoClipped(dragBoxStart); Vector2 size = mousePos - startPos; if (size.x < 0) { startPos.x += size.x; size.x = Mathf.Abs(size.x); } if (size.y < 0) { startPos.y += size.y; size.y = Mathf.Abs(size.y); } Rect r = new Rect(startPos, size); if (windowRect.Overlaps(r)) { preSelection.Add(node); } } //Check if we are hovering any of this nodes ports //Check input ports foreach (Siccity.XNode.NodePort input in node.Inputs) { //Check if port rect is available if (!portConnectionPoints.ContainsKey(input)) { continue; } Rect r = GridToWindowRect(portConnectionPoints[input]); if (r.Contains(mousePos)) { hoveredPort = input; } } //Check all output ports foreach (Siccity.XNode.NodePort output in node.Outputs) { //Check if port rect is available if (!portConnectionPoints.ContainsKey(output)) { continue; } Rect r = GridToWindowRect(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); //If a change in hash 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 (nodeHash != 0) { if (onValidate != null && nodeHash != Selection.activeObject.GetHashCode()) { onValidate.Invoke(Selection.activeObject, null); } } }
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); } }
public void Controls() { wantsMouseMove = true; Event e = Event.current; switch (e.type) { case EventType.MouseMove: break; case EventType.ScrollWheel: if (e.delta.y > 0) { zoom += 0.1f * zoom; } else { zoom -= 0.1f * zoom; } break; case EventType.MouseDrag: if (e.button == 0) { if (IsDraggingPort) { if (IsHoveringPort && hoveredPort.IsInput) { if (!draggedOutput.IsConnectedTo(hoveredPort)) { draggedOutputTarget = hoveredPort; } } else { draggedOutputTarget = null; } Repaint(); } else if (currentActivity == NodeActivity.HoldHeader || currentActivity == NodeActivity.DragHeader) { for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; node.position = WindowToGridPosition(e.mousePosition) + dragOffset[i]; bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap; if (e.control) { gridSnap = !gridSnap; } if (gridSnap) { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } } } currentActivity = NodeActivity.DragHeader; Repaint(); } else if (currentActivity == NodeActivity.HoldGrid) { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); } else if (currentActivity == NodeActivity.DragGrid) { foreach (XNode.Node node in graph.nodes) { } Repaint(); } } else if (e.button == 1 || e.button == 2) { Vector2 tempOffset = panOffset; tempOffset += e.delta * zoom; // Round value to increase crispyness of UI text tempOffset.x = Mathf.Round(tempOffset.x); tempOffset.y = Mathf.Round(tempOffset.y); panOffset = tempOffset; isPanning = true; } break; case EventType.MouseDown: Repaint(); if (e.button == 0) { if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; } else { hoveredPort.VerifyConnections(); if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; hoveredPort.Disconnect(output); draggedOutput = output; draggedOutputTarget = hoveredPort; if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { // If mousedown on node header, select or deselect if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, e.control || e.shift); } else if (e.control || e.shift) { DeselectNode(hoveredNode); } e.Use(); currentActivity = NodeActivity.HoldHeader; dragOffset = new Vector2[Selection.objects.Length]; for (int i = 0; i < dragOffset.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; dragOffset[i] = node.position - WindowToGridPosition(e.mousePosition); } } } // If mousedown on grid background, deselect all else if (!IsHoveringNode) { currentActivity = NodeActivity.HoldGrid; if (!e.control && !e.shift) { Selection.activeObject = null; } } } break; case EventType.MouseUp: if (e.button == 0) { //Port drag release if (IsDraggingPort) { //If connection is valid, save it if (draggedOutputTarget != null) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) { draggedOutput.Connect(draggedOutputTarget); } if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(graph); } //Release dragged connection draggedOutput = null; draggedOutputTarget = null; EditorUtility.SetDirty(graph); AssetDatabase.SaveAssets(); } else if (currentActivity == NodeActivity.DragHeader) { AssetDatabase.SaveAssets(); } else if (!IsHoveringNode) { // If click outside node, release field focus if (!isPanning) { // I've got no idea which of these do what, so we'll just reset all of it. GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; EditorGUIUtility.editingTextField = false; EditorGUIUtility.keyboardControl = 0; EditorGUIUtility.hotControl = 0; } AssetDatabase.SaveAssets(); } // If click node header, select single node. if (currentActivity == NodeActivity.HoldHeader && !(e.control || e.shift)) { SelectNode(hoveredNode, false); } Repaint(); currentActivity = NodeActivity.Idle; } else if (e.button == 1) { if (!isPanning) { if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, false); } ShowNodeContextMenu(); } else if (!IsHoveringNode) { ShowGraphContextMenu(); } } isPanning = false; } break; case EventType.KeyDown: if (EditorGUIUtility.editingTextField) { break; } else if (e.keyCode == KeyCode.F) { Home(); } break; case EventType.ValidateCommand: if (e.commandName == "SoftDelete") { RemoveSelectedNodes(); } else if (e.commandName == "Duplicate") { DublicateSelectedNodes(); } Repaint(); break; case EventType.Ignore: // If release mouse outside window if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { Repaint(); currentActivity = NodeActivity.Idle; } break; } }
/// <summary> Make a field for a serialized property. Manual node port override. </summary> public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) { throw new NullReferenceException(); } // If property is not a port, display a regular property field if (port == null) { EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); } else { Rect rect = new Rect(); List <PropertyAttribute> propertyAttributes = NodeEditorUtilities.GetCachedPropertyAttribs(port.node.GetType(), property.name); // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == XNode.NodePort.IO.Input) { // Get data from [Input] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.InputAttribute inputAttribute; bool dynamicPortList = false; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) { dynamicPortList = inputAttribute.dynamicPortList; showBacking = inputAttribute.backingValue; } bool usePropertyAttributes = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); float spacePadding = 0; foreach (var attr in propertyAttributes) { if (attr is SpaceAttribute) { if (usePropertyAttributes) { GUILayout.Space((attr as SpaceAttribute).height); } else { spacePadding += (attr as SpaceAttribute).height; } } else if (attr is HeaderAttribute) { if (usePropertyAttributes) { //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; position = EditorGUI.IndentedRect(position); GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); } else { spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; } } } if (dynamicPortList) { Type type = GetType(property); XNode.Node.ConnectionType connectionType = inputAttribute != null ? inputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } switch (showBacking) { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) { EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName)); } // Display an editable property field if port is not connected else { EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); } break; case XNode.Node.ShowBackingValue.Never: // Display a label EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName)); break; case XNode.Node.ShowBackingValue.Always: // Display an editable property field EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); break; } rect = GUILayoutUtility.GetLastRect(); rect.position = rect.position - new Vector2(16, -spacePadding); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.OutputAttribute outputAttribute; bool dynamicPortList = false; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) { dynamicPortList = outputAttribute.dynamicPortList; showBacking = outputAttribute.backingValue; } bool usePropertyAttributes = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); float spacePadding = 0; foreach (var attr in propertyAttributes) { if (attr is SpaceAttribute) { if (usePropertyAttributes) { GUILayout.Space((attr as SpaceAttribute).height); } else { spacePadding += (attr as SpaceAttribute).height; } } else if (attr is HeaderAttribute) { if (usePropertyAttributes) { //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; position = EditorGUI.IndentedRect(position); GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); } else { spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; } } } if (dynamicPortList) { Type type = GetType(property); XNode.Node.ConnectionType connectionType = outputAttribute != null ? outputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } switch (showBacking) { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) { EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); } // Display an editable property field if port is not connected else { EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); } break; case XNode.Node.ShowBackingValue.Never: // Display a label EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); break; case XNode.Node.ShowBackingValue.Always: // Display an editable property field EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); break; } rect = GUILayoutUtility.GetLastRect(); rect.position = rect.position + new Vector2(rect.width, spacePadding); } rect.size = new Vector2(16, 16); NodeEditor editor = NodeEditor.GetEditor(port.node, NodeEditorWindow.current); Color backgroundColor = editor.GetTint(); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); // Register the handle position Vector2 portPos = rect.center; NodeEditor.portPositions[port] = portPos; } }
public void Controls() { wantsMouseMove = true; Event e = Event.current; switch (e.type) { case EventType.MouseMove: break; case EventType.ScrollWheel: if (e.delta.y > 0) { zoom += 0.1f * zoom; } else { zoom -= 0.1f * zoom; } break; case EventType.MouseDrag: if (e.button == 0) { if (IsDraggingPort) { if (IsHoveringPort && hoveredPort.IsInput) { if (!draggedOutput.IsConnectedTo(hoveredPort)) { draggedOutputTarget = hoveredPort; } } else { draggedOutputTarget = null; } Repaint(); } else if (IsDraggingNode) { draggedNode.position = WindowToGridPosition(e.mousePosition) + dragOffset; Repaint(); } } else if (e.button == 1) { panOffset += e.delta * zoom; isPanning = true; } break; case EventType.KeyDown: if (GUIUtility.keyboardControl == 0) { break; } else if (e.keyCode == KeyCode.F) { Home(); } break; case EventType.MouseDown: Repaint(); if (e.button == 0) { SelectNode(hoveredNode); if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; } else { hoveredPort.VerifyConnections(); if (hoveredPort.IsConnected) { Node node = hoveredPort.node; NodePort output = hoveredPort.Connection; hoveredPort.Disconnect(output); draggedOutput = output; draggedOutputTarget = hoveredPort; if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { draggedNode = hoveredNode; dragOffset = hoveredNode.position - WindowToGridPosition(e.mousePosition); } } break; case EventType.MouseUp: if (e.button == 0) { //Port drag release if (IsDraggingPort) { //If connection is valid, save it if (draggedOutputTarget != null) { Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) { draggedOutput.Connect(draggedOutputTarget); } if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(graph); } //Release dragged connection draggedOutput = null; draggedOutputTarget = null; EditorUtility.SetDirty(graph); Repaint(); AssetDatabase.SaveAssets(); } else if (IsDraggingNode) { draggedNode = null; AssetDatabase.SaveAssets(); } else if (GUIUtility.hotControl != 0) { AssetDatabase.SaveAssets(); } } else if (e.button == 1) { if (!isPanning) { ShowContextMenu(); } isPanning = false; } break; } }
public void Controls() { wantsMouseMove = true; Event e = Event.current; switch (e.type) { case EventType.MouseMove: break; case EventType.ScrollWheel: if (e.delta.y > 0) { zoom += 0.1f * zoom; } else { zoom -= 0.1f * zoom; } break; case EventType.MouseDrag: if (e.button == 0) { if (IsDraggingPort) { if (IsHoveringPort) { if (hoveredPort.IsInput) { if (!draggedOutput.IsConnectedTo(hoveredPort)) { draggedOutputTarget = hoveredPort; } } } else { draggedOutputTarget = null; } Repaint(); } else if (currentActivity == NodeActivity.HoldNode) { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); } if (currentActivity == NodeActivity.DragNode) { // Holding ctrl inverts grid snap bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap; if (e.control) { gridSnap = !gridSnap; } Vector2 mousePos = WindowToGridPosition(e.mousePosition); // Move selected nodes with offset for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; Vector2 initial = node.position; node.position = mousePos + dragOffset[i]; if (gridSnap) { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame. Vector2 offset = node.position - initial; if (offset.sqrMagnitude > 0) { foreach (XNode.NodePort output in node.Outputs) { Rect rect; if (portConnectionPoints.TryGetValue(output, out rect)) { rect.position += offset; portConnectionPoints[output] = rect; } } foreach (XNode.NodePort input in node.Inputs) { Rect rect; if (portConnectionPoints.TryGetValue(input, out rect)) { rect.position += offset; portConnectionPoints[input] = rect; } } } } } // Move selected reroutes with offset for (int i = 0; i < selectedReroutes.Count; i++) { Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i]; if (gridSnap) { pos.x = (Mathf.Round(pos.x / 16) * 16); pos.y = (Mathf.Round(pos.y / 16) * 16); } selectedReroutes[i].SetPoint(pos); } Repaint(); } else if (currentActivity == NodeActivity.HoldGrid) { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; preBoxSelectionReroute = selectedReroutes.ToArray(); dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); } else if (currentActivity == NodeActivity.DragGrid) { Vector2 boxStartPos = GridToWindowPosition(dragBoxStart); Vector2 boxSize = e.mousePosition - 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); } selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } } else if (e.button == 1 || e.button == 2) { Vector2 tempOffset = panOffset; tempOffset += e.delta * zoom; // Round value to increase crispyness of UI text tempOffset.x = Mathf.Round(tempOffset.x); tempOffset.y = Mathf.Round(tempOffset.y); panOffset = tempOffset; isPanning = true; } break; case EventType.MouseDown: Repaint(); if (e.button == 0) { draggedOutputReroutes.Clear(); if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; } else { hoveredPort.VerifyConnections(); if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex); hoveredPort.Disconnect(output); draggedOutput = output; draggedOutputTarget = hoveredPort; if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { // If mousedown on node header, select or deselect if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, e.control || e.shift); if (!e.control && !e.shift) { selectedReroutes.Clear(); } } else if (e.control || e.shift) { DeselectNode(hoveredNode); } e.Use(); currentActivity = NodeActivity.HoldNode; } else if (IsHoveringReroute) { // If reroute isn't selected if (!selectedReroutes.Contains(hoveredReroute)) { // Add it if (e.control || e.shift) { selectedReroutes.Add(hoveredReroute); } // Select it else { selectedReroutes = new List <RerouteReference>() { hoveredReroute }; Selection.activeObject = null; } } // Deselect else if (e.control || e.shift) { selectedReroutes.Remove(hoveredReroute); } e.Use(); currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all else if (!IsHoveringNode) { currentActivity = NodeActivity.HoldGrid; if (!e.control && !e.shift) { selectedReroutes.Clear(); Selection.activeObject = null; } if (e.clickCount == 2) { // Add a new node on double click // Start by making it the "ObjectSelector" node var pos = WindowToGridPosition(Event.current.mousePosition); var offset = new Vector2(-110, -30); ObjectSelectorEditor.Position = pos + offset; CreateNode(typeof(ObjectSelector), pos + offset); e.Use(); } } } break; case EventType.MouseUp: if (e.button == 0) { //Port drag release if (IsDraggingPort) { //If connection is valid, save it if (draggedOutputTarget != null) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) { draggedOutput.Connect(draggedOutputTarget); } // ConnectionIndex can be -1 if the connection is removed instantly after creation int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget); if (connectionIndex != -1) { draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(graph); } } //Release dragged connection draggedOutput = null; draggedOutputTarget = null; EditorUtility.SetDirty(graph); if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } else if (currentActivity == NodeActivity.DragNode) { IEnumerable <XNode.Node> nodes = Selection.objects.Where(x => x is XNode.Node).Select(x => x as XNode.Node); foreach (XNode.Node node in nodes) { EditorUtility.SetDirty(node); } if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } else if (!IsHoveringNode) { // If click outside node, release field focus if (!isPanning) { EditorGUI.FocusTextInControl(null); } if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } // If click node header, select it. if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { selectedReroutes.Clear(); SelectNode(hoveredNode, false); } // If click reroute, select it. if (IsHoveringReroute && !(e.control || e.shift)) { selectedReroutes = new List <RerouteReference>() { hoveredReroute }; Selection.activeObject = null; } Repaint(); currentActivity = NodeActivity.Idle; } else if (e.button == 1 || e.button == 2) { if (!isPanning) { if (IsDraggingPort) { draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition)); } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) { selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint()); selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1); } else if (IsHoveringReroute) { ShowRerouteContextMenu(hoveredReroute); } else if (IsHoveringPort) { ShowPortContextMenu(hoveredPort); } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, false); } ShowNodeContextMenu(); } else if (!IsHoveringNode) { ShowGraphContextMenu(); } } isPanning = false; } break; case EventType.KeyDown: if (EditorGUIUtility.editingTextField) { break; } else if (e.keyCode == KeyCode.F) { Home(); } if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX) { if (e.keyCode == KeyCode.Return) { RenameSelectedNode(); } } else { if (e.keyCode == KeyCode.F2) { RenameSelectedNode(); } } break; case EventType.ValidateCommand: if (e.commandName == "SoftDelete") { RemoveSelectedNodes(); } else if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX && e.commandName == "Delete") { RemoveSelectedNodes(); } else if (e.commandName == "Duplicate") { DublicateSelectedNodes(); } Repaint(); break; case EventType.Ignore: // If release mouse outside window if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { Repaint(); currentActivity = NodeActivity.Idle; } break; } }
/// <summary> Make a field for a serialized property. Manual node port override. </summary> public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) { throw new NullReferenceException(); } // If property is not a port, display a regular property field if (port == null) { EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); } else { Rect rect = new Rect(); float spacePadding = 0; SpaceAttribute spaceAttribute; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out spaceAttribute)) { spacePadding = spaceAttribute.height; } // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == XNode.NodePort.IO.Input) { // Get data from [Input] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.InputAttribute inputAttribute; bool dynamicPortList = false; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) { dynamicPortList = inputAttribute.dynamicPortList; showBacking = inputAttribute.backingValue; } //Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField bool useLayoutSpace = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); if (spacePadding > 0 && useLayoutSpace) { GUILayout.Space(spacePadding); spacePadding = 0; } if (dynamicPortList) { Type type = GetType(property); XNode.Node.ConnectionType connectionType = inputAttribute != null ? inputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } switch (showBacking) { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) { EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName)); } // Display an editable property field if port is not connected else { EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); } break; case XNode.Node.ShowBackingValue.Never: // Display a label EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName)); break; case XNode.Node.ShowBackingValue.Always: // Display an editable property field EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); break; } rect = GUILayoutUtility.GetLastRect(); rect.position = rect.position - new Vector2(16, -spacePadding); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute XNode.Node.ShowBackingValue showBacking = XNode.Node.ShowBackingValue.Unconnected; XNode.Node.OutputAttribute outputAttribute; bool dynamicPortList = false; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) { dynamicPortList = outputAttribute.dynamicPortList; showBacking = outputAttribute.backingValue; } //Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField bool useLayoutSpace = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); if (spacePadding > 0 && useLayoutSpace) { GUILayout.Space(spacePadding); spacePadding = 0; } if (dynamicPortList) { Type type = GetType(property); XNode.Node.ConnectionType connectionType = outputAttribute != null ? outputAttribute.connectionType : XNode.Node.ConnectionType.Multiple; DynamicPortList(property.name, type, property.serializedObject, port.direction, connectionType); return; } switch (showBacking) { case XNode.Node.ShowBackingValue.Unconnected: // Display a label if port is connected if (port.IsConnected) { EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); } // Display an editable property field if port is not connected else { EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); } break; case XNode.Node.ShowBackingValue.Never: // Display a label EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); break; case XNode.Node.ShowBackingValue.Always: // Display an editable property field EditorGUILayout.PropertyField(property, label, includeChildren, GUILayout.MinWidth(30)); break; } rect = GUILayoutUtility.GetLastRect(); rect.position = rect.position + new Vector2(rect.width, spacePadding); } rect.size = new Vector2(16, 16); NodeEditor editor = NodeEditor.GetEditor(port.node, NodeEditorWindow.current); Color backgroundColor = editor.GetTint(); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); // Register the handle position Vector2 portPos = rect.center; NodeEditor.portPositions[port] = portPos; } }
private void DrawNodes() { Event e = Event.current; if (e.type == EventType.Repaint) { portConnectionPoints.Clear(); nodeWidths.Clear(); } //Selected node is hashed before and after node GUI to detect changes int nodeHash = 0; System.Reflection.MethodInfo onValidate = null; if (selectedNode != null) { onValidate = selectedNode.GetType().GetMethod("OnValidate"); if (onValidate != null) { nodeHash = selectedNode.GetHashCode(); } } BeginZoomed(position, zoom); Vector2 mousePos = Event.current.mousePosition; if (e.type != EventType.Layout) { hoveredNode = null; hoveredPort = null; } //Save guiColor so we can revert it Color guiColor = GUI.color; for (int n = 0; n < graph.nodes.Count; n++) { while (graph.nodes[n] == null) { graph.nodes.RemoveAt(n); } if (n >= graph.nodes.Count) { return; } Node node = graph.nodes[n]; NodeEditor nodeEditor = NodeEditor.GetEditor(node); NodeEditor.portPositions = new Dictionary <NodePort, Vector2>(); //Get node position Vector2 nodePos = GridToWindowPositionNoClipped(node.position); GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000))); GUIStyle style = NodeEditorResources.styles.nodeBody; GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(new GUIStyle(style)); GUI.color = guiColor; EditorGUI.BeginChangeCheck(); //Draw node contents nodeEditor.OnNodeGUI(); //Apply nodeEditor.serializedObject.ApplyModifiedProperties(); //If user changed a value, notify other scripts through onUpdateNode if (EditorGUI.EndChangeCheck()) { if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } if (e.type == EventType.Repaint) { nodeWidths.Add(node, nodeEditor.GetWidth()); 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.Add(kvp.Key, rect); } } 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; } //Check if we are hovering any of this nodes ports //Check input ports foreach (NodePort input in node.Inputs) { //Check if port rect is available if (!portConnectionPoints.ContainsKey(input)) { continue; } Rect r = GridToWindowRect(portConnectionPoints[input]); if (r.Contains(mousePos)) { hoveredPort = input; } } //Check all output ports foreach (NodePort output in node.Outputs) { //Check if port rect is available if (!portConnectionPoints.ContainsKey(output)) { continue; } Rect r = GridToWindowRect(portConnectionPoints[output]); if (r.Contains(mousePos)) { hoveredPort = output; } } } GUILayout.EndArea(); } EndZoomed(position, zoom); //If a change in hash 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 (selectedNode != null) { if (onValidate != null && nodeHash != selectedNode.GetHashCode()) { onValidate.Invoke(selectedNode, null); } } }
public void Controls() { this.wantsMouseMove = true; var e = Event.current; switch (e.type) { case EventType.DragUpdated: case EventType.DragPerform: DragAndDrop.visualMode = DragAndDropVisualMode.Generic; if (e.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); this.graphEditor.OnDropObjects(DragAndDrop.objectReferences); } break; case EventType.MouseMove: //Keyboard commands will not get correct mouse position from Event this.lastMousePosition = e.mousePosition; break; case EventType.ScrollWheel: var oldZoom = this.zoom; if (e.delta.y > 0) { this.zoom += 0.1f * this.zoom; } else { this.zoom -= 0.1f * this.zoom; } if (NodeEditorPreferences.GetSettings().zoomToMouse) { this.panOffset += (1 - oldZoom / this.zoom) * (this.WindowToGridPosition(e.mousePosition) + this.panOffset); } break; case EventType.MouseDrag: switch (e.button) { case 0: { if (this.IsDraggingPort) { // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously if (this.IsHoveringPort && this.hoveredPort.IsInput && !this.draggedOutput.IsConnectedTo(this.hoveredPort)) { this.draggedOutputTarget = this.hoveredPort; } else { this.draggedOutputTarget = null; } this.Repaint(); } else if (currentActivity == NodeActivity.HoldNode) { this.RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; this.Repaint(); } switch (currentActivity) { case NodeActivity.DragNode: { // Holding ctrl inverts grid snap var gridSnap = NodeEditorPreferences.GetSettings().gridSnap; if (e.control) { gridSnap = !gridSnap; } var mousePos = this.WindowToGridPosition(e.mousePosition); // Move selected nodes with offset for (var i = 0; i < Selection.objects.Length; i++) { if (!(Selection.objects[i] is xNode.Node node)) { continue; } Undo.RecordObject(node, "Moved Node"); var initial = node.position; node.position = mousePos + dragOffset[i]; if (gridSnap) { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } // Offset portConnectionPoints instantly if a node is dragged so they aren't delayed by a frame. var offset = node.position - initial; if (!(offset.sqrMagnitude > 0)) { continue; } foreach (var output in node.Outputs) { if (this.portConnectionPoints.TryGetValue(output, out var rect)) { rect.position += offset; this.portConnectionPoints[output] = rect; } } foreach (var input in node.Inputs) { if (this.portConnectionPoints.TryGetValue(input, out var rect)) { rect.position += offset; this.portConnectionPoints[input] = rect; } } } // Move selected reroutes with offset for (var i = 0; i < this.selectedReroutes.Count; i++) { var pos = mousePos + dragOffset[Selection.objects.Length + i]; if (gridSnap) { pos.x = (Mathf.Round(pos.x / 16) * 16); pos.y = (Mathf.Round(pos.y / 16) * 16); } this.selectedReroutes[i].SetPoint(pos); } this.Repaint(); break; } case NodeActivity.HoldGrid: currentActivity = NodeActivity.DragGrid; this.preBoxSelection = Selection.objects; this.preBoxSelectionReroute = this.selectedReroutes.ToArray(); this.dragBoxStart = this.WindowToGridPosition(e.mousePosition); this.Repaint(); break; case NodeActivity.DragGrid: { var boxStartPos = this.GridToWindowPosition(this.dragBoxStart); var boxSize = e.mousePosition - 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); } this.selectionBox = new Rect(boxStartPos, boxSize); this.Repaint(); break; } } break; } case 1: case 2: { //check drag threshold for larger screens if (e.delta.magnitude > this.dragThreshold) { this.panOffset += e.delta * this.zoom; isPanning = true; } break; } } break; case EventType.MouseDown: this.Repaint(); if (e.button == 0) { this.draggedOutputReroutes.Clear(); if (this.IsHoveringPort) { if (this.hoveredPort.IsOutput) { this.draggedOutput = this.hoveredPort; this.autoConnectOutput = this.hoveredPort; } else { this.hoveredPort.VerifyConnections(); this.autoConnectOutput = null; if (this.hoveredPort.IsConnected) { var node = this.hoveredPort.node; var output = this.hoveredPort.Connection; var outputConnectionIndex = output.GetConnectionIndex(this.hoveredPort); this.draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex); this.hoveredPort.Disconnect(output); this.draggedOutput = output; this.draggedOutputTarget = this.hoveredPort; if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } } } else if (this.IsHoveringNode && this.IsHoveringTitle(this.hoveredNode)) { // If mousedown on node header, select or deselect if (!Selection.Contains(this.hoveredNode)) { this.SelectNode(this.hoveredNode, e.control || e.shift); if (!e.control && !e.shift) { this.selectedReroutes.Clear(); } } else if (e.control || e.shift) { this.DeselectNode(this.hoveredNode); } // Cache double click state, but only act on it in MouseUp - Except ClickCount only works in mouseDown. this.isDoubleClick = (e.clickCount == 2); e.Use(); currentActivity = NodeActivity.HoldNode; } else if (this.IsHoveringReroute) { // If reroute isn't selected if (!this.selectedReroutes.Contains(this.hoveredReroute)) { // Add it if (e.control || e.shift) { this.selectedReroutes.Add(this.hoveredReroute); } // Select it else { this.selectedReroutes = new List <RerouteReference>() { this.hoveredReroute }; Selection.activeObject = null; } } // Deselect else if (e.control || e.shift) { this.selectedReroutes.Remove(this.hoveredReroute); } e.Use(); currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all else if (!this.IsHoveringNode) { currentActivity = NodeActivity.HoldGrid; if (!e.control && !e.shift) { this.selectedReroutes.Clear(); Selection.activeObject = null; } } } break; case EventType.MouseUp: if (e.button == 0) { //Port drag release if (this.IsDraggingPort) { // If connection is valid, save it if (this.draggedOutputTarget != null && this.draggedOutput.CanConnectTo(this.draggedOutputTarget)) { var node = this.draggedOutputTarget.node; if (this.graph.nodes.Count != 0) { this.draggedOutput.Connect(this.draggedOutputTarget); } // ConnectionIndex can be -1 if the connection is removed instantly after creation var connectionIndex = this.draggedOutput.GetConnectionIndex(this.draggedOutputTarget); if (connectionIndex != -1) { this.draggedOutput.GetReroutePoints(connectionIndex).AddRange(this.draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(this.graph); } } // Open context menu for auto-connection if there is no target node else if (this.draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate&& this.autoConnectOutput != null) { var menu = new GenericMenu(); this.graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } //Release dragged connection this.draggedOutput = null; this.draggedOutputTarget = null; EditorUtility.SetDirty(this.graph); if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } else if (currentActivity == NodeActivity.DragNode) { var nodes = Selection.objects.Where(x => x is xNode.Node).Select(x => x as xNode.Node); foreach (var node in nodes) { EditorUtility.SetDirty(node); } if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } else if (!this.IsHoveringNode) { // If click outside node, release field focus if (!isPanning) { EditorGUI.FocusTextInControl(null); EditorGUIUtility.editingTextField = false; } if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } } // If click node header, select it. if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { this.selectedReroutes.Clear(); this.SelectNode(this.hoveredNode, false); // Double click to center node if (this.isDoubleClick) { var nodeDimension = this.nodeSizes.ContainsKey(this.hoveredNode) ? this.nodeSizes[this.hoveredNode] / 2 : Vector2.zero; this.panOffset = -this.hoveredNode.position - nodeDimension; } } // If click reroute, select it. if (this.IsHoveringReroute && !(e.control || e.shift)) { this.selectedReroutes = new List <RerouteReference>() { this.hoveredReroute }; Selection.activeObject = null; } this.Repaint(); currentActivity = NodeActivity.Idle; } else if (e.button == 1 || e.button == 2) { if (!isPanning) { if (this.IsDraggingPort) { this.draggedOutputReroutes.Add(this.WindowToGridPosition(e.mousePosition)); } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && this.selectedReroutes.Count == 1) { this.selectedReroutes[0].InsertPoint(this.selectedReroutes[0].GetPoint()); this.selectedReroutes[0] = new RerouteReference(this.selectedReroutes[0].port, this.selectedReroutes[0].connectionIndex, this.selectedReroutes[0].pointIndex + 1); } else if (this.IsHoveringReroute) { this.ShowRerouteContextMenu(this.hoveredReroute); } else if (this.IsHoveringPort) { this.ShowPortContextMenu(this.hoveredPort); } else if (this.IsHoveringNode && this.IsHoveringTitle(this.hoveredNode)) { if (!Selection.Contains(this.hoveredNode)) { this.SelectNode(this.hoveredNode, false); } this.autoConnectOutput = null; var menu = new GenericMenu(); NodeEditor.GetEditor(this.hoveredNode, this).AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places. } else if (!this.IsHoveringNode) { this.autoConnectOutput = null; var menu = new GenericMenu(); this.graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } } isPanning = false; } // Reset DoubleClick this.isDoubleClick = false; break; case EventType.KeyDown: if (EditorGUIUtility.editingTextField) { break; } else if (e.keyCode == KeyCode.F) { this.Home(); } if (NodeEditorUtilities.IsMac()) { if (e.keyCode == KeyCode.Return) { this.RenameSelectedNode(); } } else { if (e.keyCode == KeyCode.F2) { this.RenameSelectedNode(); } } if (e.keyCode == KeyCode.A) { if (Selection.objects.Any(x => this.graph.nodes.Contains(x as xNode.Node))) { foreach (var node in this.graph.nodes) { this.DeselectNode(node); } } else { foreach (var node in this.graph.nodes) { this.SelectNode(node, true); } } this.Repaint(); } break; case EventType.ValidateCommand: case EventType.ExecuteCommand: if (e.commandName == "SoftDelete") { if (e.type == EventType.ExecuteCommand) { this.RemoveSelectedNodes(); } e.Use(); } else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") { if (e.type == EventType.ExecuteCommand) { this.RemoveSelectedNodes(); } e.Use(); } else if (e.commandName == "Duplicate") { if (e.type == EventType.ExecuteCommand) { this.DuplicateSelectedNodes(); } e.Use(); } else if (e.commandName == "Copy") { if (e.type == EventType.ExecuteCommand) { this.CopySelectedNodes(); } e.Use(); } else if (e.commandName == "Paste") { if (e.type == EventType.ExecuteCommand) { this.PasteNodes(this.WindowToGridPosition(this.lastMousePosition)); } e.Use(); } this.Repaint(); break; case EventType.Ignore: // If release mouse outside window if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { this.Repaint(); currentActivity = NodeActivity.Idle; } break; } }
public void Controls() { wantsMouseMove = true; Event e = Event.current; switch (e.type) { case EventType.MouseMove: break; case EventType.ScrollWheel: if (e.delta.y > 0) { zoom += 0.1f * zoom; } else { zoom -= 0.1f * zoom; } break; case EventType.MouseDrag: if (e.button == 0) { if (IsDraggingPort) { if (IsHoveringPort && hoveredPort.IsInput) { if (!draggedOutput.IsConnectedTo(hoveredPort)) { draggedOutputTarget = hoveredPort; } } else { draggedOutputTarget = null; } Repaint(); } else if (currentActivity == NodeActivity.HoldNode) { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); } if (currentActivity == NodeActivity.DragNode) { // Holding ctrl inverts grid snap bool gridSnap = NodeEditorPreferences.GetSettings().gridSnap; if (e.control) { gridSnap = !gridSnap; } Vector2 mousePos = WindowToGridPosition(e.mousePosition); // Move selected nodes with offset for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; node.position = mousePos + dragOffset[i]; if (gridSnap) { node.position.x = (Mathf.Round((node.position.x + 8) / 16) * 16) - 8; node.position.y = (Mathf.Round((node.position.y + 8) / 16) * 16) - 8; } } } // Move selected reroutes with offset for (int i = 0; i < selectedReroutes.Count; i++) { Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i]; pos.x -= 8; pos.y -= 8; if (gridSnap) { pos.x = (Mathf.Round((pos.x + 8) / 16) * 16); pos.y = (Mathf.Round((pos.y + 8) / 16) * 16); } selectedReroutes[i].SetPoint(pos); } Repaint(); } else if (currentActivity == NodeActivity.HoldGrid) { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; preBoxSelectionReroute = selectedReroutes.ToArray(); dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); } else if (currentActivity == NodeActivity.DragGrid) { Vector2 boxStartPos = GridToWindowPosition(dragBoxStart); Vector2 boxSize = e.mousePosition - 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); } selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } } else if (e.button == 1 || e.button == 2) { Vector2 tempOffset = panOffset; tempOffset += e.delta * zoom; // Round value to increase crispyness of UI text tempOffset.x = Mathf.Round(tempOffset.x); tempOffset.y = Mathf.Round(tempOffset.y); panOffset = tempOffset; isPanning = true; } break; case EventType.MouseDown: Repaint(); if (e.button == 0) { draggedOutputReroutes.Clear(); if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; } else { hoveredPort.VerifyConnections(); if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex); hoveredPort.Disconnect(output); draggedOutput = output; draggedOutputTarget = hoveredPort; if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { // If mousedown on node header, select or deselect if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, e.control || e.shift); if (!e.control && !e.shift) { selectedReroutes.Clear(); } } else if (e.control || e.shift) { DeselectNode(hoveredNode); } e.Use(); currentActivity = NodeActivity.HoldNode; } else if (IsHoveringReroute) { // If reroute isn't selected if (!selectedReroutes.Contains(hoveredReroute)) { // Add it if (e.control || e.shift) { selectedReroutes.Add(hoveredReroute); } // Select it else { selectedReroutes = new List <RerouteReference>() { hoveredReroute }; Selection.activeObject = null; } } // Deselect else if (e.control || e.shift) { selectedReroutes.Remove(hoveredReroute); } e.Use(); currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all else if (!IsHoveringNode) { currentActivity = NodeActivity.HoldGrid; if (!e.control && !e.shift) { selectedReroutes.Clear(); Selection.activeObject = null; } } } break; case EventType.MouseUp: if (e.button == 0) { //Port drag release if (IsDraggingPort) { //If connection is valid, save it if (draggedOutputTarget != null) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) { draggedOutput.Connect(draggedOutputTarget); } // ConnectionIndex can be -1 if the connection is removed instantly after creation int connectionIndex = draggedOutput.GetConnectionIndex(draggedOutputTarget); if (connectionIndex != -1) { draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) { NodeEditor.onUpdateNode(node); } EditorUtility.SetDirty(graph); } } //Release dragged connection draggedOutput = null; draggedOutputTarget = null; EditorUtility.SetDirty(graph); AssetDatabase.SaveAssets(); } else if (currentActivity == NodeActivity.DragNode) { AssetDatabase.SaveAssets(); } else if (!IsHoveringNode) { // If click outside node, release field focus if (!isPanning) { // I've got no idea which of these do what, so we'll just reset all of it. GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; EditorGUIUtility.editingTextField = false; EditorGUIUtility.keyboardControl = 0; EditorGUIUtility.hotControl = 0; } AssetDatabase.SaveAssets(); } // If click node header, select it. if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { selectedReroutes.Clear(); SelectNode(hoveredNode, false); } // If click reroute, select it. if (IsHoveringReroute && !(e.control || e.shift)) { selectedReroutes = new List <RerouteReference>() { hoveredReroute }; Selection.activeObject = null; } Repaint(); currentActivity = NodeActivity.Idle; } else if (e.button == 1) { if (!isPanning) { if (IsDraggingPort) { draggedOutputReroutes.Add(WindowToGridPosition(e.mousePosition)); } else if (currentActivity == NodeActivity.DragNode && Selection.activeObject == null && selectedReroutes.Count == 1) { selectedReroutes[0].InsertPoint(selectedReroutes[0].GetPoint()); selectedReroutes[0] = new RerouteReference(selectedReroutes[0].port, selectedReroutes[0].connectionIndex, selectedReroutes[0].pointIndex + 1); } else if (IsHoveringReroute) { ShowRerouteContextMenu(hoveredReroute); } else if (IsHoveringPort) { ShowPortContextMenu(hoveredPort); } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { if (!Selection.Contains(hoveredNode)) { SelectNode(hoveredNode, false); } ShowNodeContextMenu(); } else if (!IsHoveringNode) { ShowGraphContextMenu(); } } isPanning = false; } break; case EventType.KeyDown: if (EditorGUIUtility.editingTextField) { break; } else if (e.keyCode == KeyCode.F) { Home(); } break; case EventType.ValidateCommand: if (e.commandName == "SoftDelete") { RemoveSelectedNodes(); } else if (e.commandName == "Duplicate") { DublicateSelectedNodes(); } Repaint(); break; case EventType.Ignore: // If release mouse outside window if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { Repaint(); currentActivity = NodeActivity.Idle; } break; } }
//private EntityActionNode eaNode; //private EntityConditionNode ecNode; void RenderDialogNodeGUI(NodeEditor nodeEditor, XNode.Node node) { EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); dn = node as DialogNode; if (dn != null) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); if (dn.showDialogText = EditorGUILayout.Foldout(dn.showDialogText, "Dialog")) { EditorGUILayout.LabelField("Dialog"); EditorGUILayout.BeginVertical(EditorStyles.helpBox); dn.dialog.text = EditorGUILayout.TextArea(dn.dialog.text, EditorStyles.wordWrappedLabel, GUILayout.MinHeight(60), GUILayout.MaxWidth(180)); EditorGUILayout.EndVertical(); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); if (dn.Options = EditorGUILayout.Foldout(dn.Options, "Options")) { EditorGUILayout.LabelField("Talk ID"); dn.dialog.TalkID = EditorGUILayout.IntSlider(dn.dialog.TalkID, 0, 6); dn.dialog.clip = (AudioClip)EditorGUILayout.ObjectField("Audio", dn.dialog.clip, typeof(AudioClip), false); if (dn.dialog.hasEvent = EditorGUILayout.Toggle("Event", dn.dialog.hasEvent)) { dn.dialog.eventID = EditorGUILayout.IntField("Event ID", dn.dialog.eventID); } dn.dialog.isEntry = EditorGUILayout.Toggle("Is Entry", dn.dialog.isEntry); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); if (dn.showDialogCondition = EditorGUILayout.Foldout(dn.showDialogCondition, "Conditions")) { if (dn.dialog.conditions == null) { dn.dialog.conditions = new DialogObject.DialogCondition(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); dn.dialog.conditions.isOneTime = EditorGUILayout.Toggle("One Shot", dn.dialog.conditions.isOneTime); if (dn.dialog.conditions.isOneTime && string.IsNullOrEmpty(dn.dialog.conditions.oneTimeID)) { dn.dialog.conditions.oneTimeID = ToolKit.GetUniqueID(); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); if (dn.showDialogVar = EditorGUILayout.Foldout(dn.showDialogVar, "Global Var")) { EditorGUILayout.LabelField(dn.dialog.conditions.varID); if (GUILayout.Button("Set Var")) { ObjectPicker.GetObject(ObjectPicker.ObjectType.Variable, dn.SetDialogVar); } dn.dialog.conditions.varState = EditorGUILayout.Toggle("State", dn.dialog.conditions.varState); } EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); if (GUILayout.Button("Add", EditorStyles.toolbarButton)) { dn.AddResponse(); } if (GUILayout.Button("Remove", EditorStyles.toolbarButton)) { dn.RemoveResponse(); } EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); { int i = 0; if (dn.selectedResponse > dn.dialog.responses.Length - 1) { dn.selectedResponse = -1; } foreach (DialogObject.DialogResponse response in dn.dialog.responses) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); EditorGUILayout.BeginHorizontal(); string s = ""; if (!string.IsNullOrEmpty(response.response)) { s = TruncateLongString(response.response, 16); } else { s = "Response " + i.ToString(); } if (GUILayout.Button(s, EditorStyles.foldout)) { if (dn.selectedResponse == i) { dn.selectedResponse = -1; } else { dn.selectedResponse = i; } } nodeEditor.OnNodeGUI(i); EditorGUILayout.EndHorizontal(); if (dn.selectedResponse == i) { if (dn.dialog.responses != null && dn.dialog.responses.Length > 0 && dn.selectedResponse >= 0) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); dn.dialog.responses[dn.selectedResponse].response = EditorGUILayout.TextArea(dn.dialog.responses[dn.selectedResponse].response, EditorStyles.wordWrappedLabel, GUILayout.MinHeight(60), GUILayout.MaxWidth(180)); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); dn.responseOptions = EditorGUILayout.Foldout(dn.responseOptions, "Options"); if (dn.responseOptions) { dn.conditionOptions = false; dn.dialog.responses[dn.selectedResponse].eventID = EditorGUILayout.IntField("Event", dn.dialog.responses[dn.selectedResponse].eventID); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); dn.conditionOptions = EditorGUILayout.Foldout(dn.conditionOptions, "Conditions"); if (dn.conditionOptions) { dn.responseOptions = false; if (dn.dialog.responses[dn.selectedResponse].conditions == null) { dn.dialog.responses[dn.selectedResponse].conditions = new DialogObject.DialogCondition(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.Space(); EditorGUILayout.BeginVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); dn.dialog.responses[dn.selectedResponse].conditions.isOneTime = EditorGUILayout.Toggle("One Shot", dn.dialog.responses[dn.selectedResponse].conditions.isOneTime); if (dn.dialog.responses[dn.selectedResponse].conditions.isOneTime && string.IsNullOrEmpty(dn.dialog.responses[dn.selectedResponse].conditions.oneTimeID)) { dn.dialog.responses[dn.selectedResponse].conditions.oneTimeID = ToolKit.GetUniqueID(); } EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); if (dn.showVar = EditorGUILayout.Foldout(dn.showVar, "Global Var")) { EditorGUILayout.LabelField(dn.dialog.responses[dn.selectedResponse].conditions.varID); if (GUILayout.Button("Set Var")) { //ObjectPicker.GetObject(ObjectPicker.ObjectType.Variable, dn.SetVar); Debug.Log("Get/set vars"); } dn.dialog.responses[dn.selectedResponse].conditions.varState = EditorGUILayout.Toggle("State", dn.dialog.responses[dn.selectedResponse].conditions.varState); } EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); } } i++; EditorGUILayout.EndVertical(); } } EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical(); EditorUtility.SetDirty(dn); } EditorGUILayout.EndVertical(); EditorGUILayout.EndHorizontal(); }