/// <summary> Make a field for a serialized property. Automatically displays relevant node port. </summary> public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) { throw new NullReferenceException(); } Siccity.XNode.Node node = property.serializedObject.targetObject as Siccity.XNode.Node; Siccity.XNode.NodePort port = node.GetPort(property.name); PropertyField(property, label, port, includeChildren); }
/// <summary> Make a simple port field. </summary> public static void PortField(GUIContent label, Siccity.XNode.NodePort port, params GUILayoutOption[] options) { if (port == null) { return; } if (label == null) { EditorGUILayout.LabelField(ObjectNames.NicifyVariableName(port.fieldName), options); } else { EditorGUILayout.LabelField(label, options); } Rect rect = GUILayoutUtility.GetLastRect(); if (port.direction == Siccity.XNode.NodePort.IO.Input) { rect.position = rect.position - new Vector2(16, 0); } else if (port.direction == Siccity.XNode.NodePort.IO.Output) { rect.position = rect.position + new Vector2(rect.width, 0); } rect.size = new Vector2(16, 16); Color backgroundColor = new Color32(90, 97, 105, 255); if (NodeEditorWindow.nodeTint.ContainsKey(port.node.GetType())) { backgroundColor *= NodeEditorWindow.nodeTint[port.node.GetType()]; } Color col = NodeEditorWindow.current.graphEditor.GetTypeColor(port.ValueType); DrawPortHandle(rect, backgroundColor, col); // Register the handle position Vector2 portPos = rect.center; if (NodeEditor.portPositions.ContainsKey(port)) { NodeEditor.portPositions[port] = portPos; } else { NodeEditor.portPositions.Add(port, portPos); } }
/// <summary> Draws all connections </summary> public void DrawConnections() { foreach (Siccity.XNode.Node node in graph.nodes) { //If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset. if (node == null) { continue; } foreach (Siccity.XNode.NodePort output in node.Outputs) { //Needs cleanup. Null checks are ugly if (!portConnectionPoints.ContainsKey(output)) { continue; } Vector2 from = _portConnectionPoints[output].center; for (int k = 0; k < output.ConnectionCount; k++) { Siccity.XNode.NodePort input = output.GetConnection(k); if (input == null) { continue; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return. } if (!input.IsConnectedTo(output)) { input.Connect(output); } if (!_portConnectionPoints.ContainsKey(input)) { continue; } Vector2 to = _portConnectionPoints[input].center; Color connectionColor = graphEditor.GetTypeColor(output.ValueType); DrawConnection(from, to, connectionColor); } } } }
/// <summary> Dublicate selected nodes and select the dublicates </summary> public void DublicateSelectedNodes() { UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length]; Dictionary<Siccity.XNode.Node, Siccity.XNode.Node> substitutes = new Dictionary<Siccity.XNode.Node, Siccity.XNode.Node>(); for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is Siccity.XNode.Node) { Siccity.XNode.Node srcNode = Selection.objects[i] as Siccity.XNode.Node; if (srcNode.graph != graph) continue; // ignore nodes selected in another graph Siccity.XNode.Node newNode = graph.CopyNode(srcNode); substitutes.Add(srcNode, newNode); newNode.position = srcNode.position + new Vector2(30, 30); newNodes[i] = newNode; } } // Walk through the selected nodes again, recreate connections, using the new nodes for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is Siccity.XNode.Node) { Siccity.XNode.Node srcNode = Selection.objects[i] as Siccity.XNode.Node; if (srcNode.graph != graph) continue; // ignore nodes selected in another graph foreach (Siccity.XNode.NodePort port in srcNode.Ports) { for (int c = 0; c < port.ConnectionCount; c++) { Siccity.XNode.NodePort inputPort = port.direction == Siccity.XNode.NodePort.IO.Input ? port : port.GetConnection(c); Siccity.XNode.NodePort outputPort = port.direction == Siccity.XNode.NodePort.IO.Output ? port : port.GetConnection(c); if (substitutes.ContainsKey(inputPort.node) && substitutes.ContainsKey(outputPort.node)) { Siccity.XNode.Node newNodeIn = substitutes[inputPort.node]; Siccity.XNode.Node newNodeOut = substitutes[outputPort.node]; newNodeIn.UpdateStaticPorts(); newNodeOut.UpdateStaticPorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); outputPort = newNodeOut.GetOutputPort(outputPort.fieldName); } if (!inputPort.IsConnectedTo(outputPort)) inputPort.Connect(outputPort); } } } } Selection.objects = newNodes; }
/// <summary> Make a field for a serialized property. Manual node port override. </summary> public static void PropertyField(SerializedProperty property, GUIContent label, Siccity.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(); // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == Siccity.XNode.NodePort.IO.Input) { // Get data from [Input] attribute Siccity.XNode.Node.ShowBackingValue showBacking = Siccity.XNode.Node.ShowBackingValue.Unconnected; Siccity.XNode.Node.InputAttribute inputAttribute; if (NodeEditorUtilities.GetAttrib(port.node.GetType(), property.name, out inputAttribute)) { showBacking = inputAttribute.backingValue; } switch (showBacking) { case Siccity.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 Siccity.XNode.Node.ShowBackingValue.Never: // Display a label EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName)); break; case Siccity.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, 0); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == Siccity.XNode.NodePort.IO.Output) { // Get data from [Output] attribute Siccity.XNode.Node.ShowBackingValue showBacking = Siccity.XNode.Node.ShowBackingValue.Unconnected; Siccity.XNode.Node.OutputAttribute outputAttribute; if (NodeEditorUtilities.GetAttrib(port.node.GetType(), property.name, out outputAttribute)) { showBacking = outputAttribute.backingValue; } switch (showBacking) { case Siccity.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 Siccity.XNode.Node.ShowBackingValue.Never: // Display a label EditorGUILayout.LabelField(label != null ? label : new GUIContent(property.displayName), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); break; case Siccity.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, 0); } rect.size = new Vector2(16, 16); Color backgroundColor = new Color32(90, 97, 105, 255); if (NodeEditorWindow.nodeTint.ContainsKey(port.node.GetType())) { backgroundColor *= NodeEditorWindow.nodeTint[port.node.GetType()]; } Color col = NodeEditorWindow.current.graphEditor.GetTypeColor(port.ValueType); DrawPortHandle(rect, backgroundColor, col); // Register the handle position Vector2 portPos = rect.center; if (NodeEditor.portPositions.ContainsKey(port)) { NodeEditor.portPositions[port] = portPos; } else { NodeEditor.portPositions.Add(port, portPos); } } }
/// <summary> Make a field for a serialized property. Manual node port override. </summary> public static void PropertyField(SerializedProperty property, Siccity.XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { PropertyField(property, null, port, includeChildren, options); }
/// <summary> Make a simple port field. </summary> public static void PortField(Siccity.XNode.NodePort port, params GUILayoutOption[] options) { PortField(null, port, options); }
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 Siccity.XNode.Node) { Siccity.XNode.Node node = Selection.objects[i] as Siccity.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 (Siccity.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) { Siccity.XNode.Node node = hoveredPort.node; Siccity.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 Siccity.XNode.Node) { Siccity.XNode.Node node = Selection.objects[i] as Siccity.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) { Siccity.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) { GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 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.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; } }