Esempio n. 1
0
        public void DeselectNode(AnimationBaker.StateMachine.XNode.Node node)
        {
            List <Object> selection = new List <Object>(Selection.objects);

            selection.Remove(node);
            Selection.objects = selection.ToArray();
        }
Esempio n. 2
0
 /// <summary> Initiate a rename on the currently selected node </summary>
 public void RenameSelectedNode()
 {
     if (Selection.objects.Length == 1 && Selection.activeObject is AnimationBaker.StateMachine.XNode.Node)
     {
         AnimationBaker.StateMachine.XNode.Node node = Selection.activeObject as AnimationBaker.StateMachine.XNode.Node;
         NodeEditor.GetEditor(node).InitiateRename();
     }
 }
Esempio n. 3
0
        /// <summary> Dublicate selected nodes and select the dublicates </summary>
        public void DublicateSelectedNodes()
        {
            UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length];
            Dictionary <AnimationBaker.StateMachine.XNode.Node, AnimationBaker.StateMachine.XNode.Node> substitutes = new Dictionary <AnimationBaker.StateMachine.XNode.Node, AnimationBaker.StateMachine.XNode.Node>();

            for (int i = 0; i < Selection.objects.Length; i++)
            {
                if (Selection.objects[i] is AnimationBaker.StateMachine.XNode.Node)
                {
                    AnimationBaker.StateMachine.XNode.Node srcNode = Selection.objects[i] as AnimationBaker.StateMachine.XNode.Node;
                    if (srcNode.graph != graph)
                    {
                        continue;                         // ignore nodes selected in another graph
                    }
                    AnimationBaker.StateMachine.XNode.Node newNode = graphEditor.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 AnimationBaker.StateMachine.XNode.Node)
                {
                    AnimationBaker.StateMachine.XNode.Node srcNode = Selection.objects[i] as AnimationBaker.StateMachine.XNode.Node;
                    if (srcNode.graph != graph)
                    {
                        continue;                         // ignore nodes selected in another graph
                    }
                    foreach (AnimationBaker.StateMachine.XNode.NodePort port in srcNode.Ports)
                    {
                        for (int c = 0; c < port.ConnectionCount; c++)
                        {
                            AnimationBaker.StateMachine.XNode.NodePort inputPort  = port.direction == AnimationBaker.StateMachine.XNode.NodePort.IO.Input ? port : port.GetConnectionPort(c);
                            AnimationBaker.StateMachine.XNode.NodePort outputPort = port.direction == AnimationBaker.StateMachine.XNode.NodePort.IO.Output ? port : port.GetConnectionPort(c);

                            if (substitutes.ContainsKey(inputPort.node) && substitutes.ContainsKey(outputPort.node))
                            {
                                AnimationBaker.StateMachine.XNode.Node newNodeIn  = substitutes[inputPort.node];
                                AnimationBaker.StateMachine.XNode.Node newNodeOut = substitutes[outputPort.node];
                                newNodeIn.UpdateStaticPorts();
                                newNodeOut.UpdateStaticPorts();
                                inputPort  = newNodeIn.GetInputPort(inputPort.fieldName);
                                outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
                            }
                            if (!inputPort.IsConnectedToPort(outputPort))
                            {
                                inputPort.Connect(outputPort);
                            }
                        }
                    }
                }
            }
            Selection.objects = newNodes;
        }
 /// <summary> Safely remove a node and all its connections. </summary>
 public void RemoveNode(AnimationBaker.StateMachine.XNode.Node node)
 {
     UnityEngine.Object.DestroyImmediate(node, true);
     target.RemoveNode(node);
     if (NodeEditorPreferences.GetSettings().autoSave)
     {
         AssetDatabase.SaveAssets();
     }
 }
Esempio n. 5
0
 /// <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();
     }
     AnimationBaker.StateMachine.XNode.Node     node = property.serializedObject.targetObject as AnimationBaker.StateMachine.XNode.Node;
     AnimationBaker.StateMachine.XNode.NodePort port = node.GetPort(property.name);
     PropertyField(property, label, port, includeChildren);
 }
Esempio n. 6
0
        /// <summary> Draw this node on top of other nodes by placing it last in the graph.nodes list </summary>
        public void MoveNodeToTop(AnimationBaker.StateMachine.XNode.Node node)
        {
            int index;

            while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1)
            {
                graph.nodes[index]     = graph.nodes[index + 1];
                graph.nodes[index + 1] = node;
            }
        }
 /// <summary> Creates a copy of the original node in the graph </summary>
 public AnimationBaker.StateMachine.XNode.Node CopyNode(AnimationBaker.StateMachine.XNode.Node original)
 {
     AnimationBaker.StateMachine.XNode.Node node = target.CopyNode(original);
     node.name = original.name;
     AssetDatabase.AddObjectToAsset(node, target);
     if (NodeEditorPreferences.GetSettings().autoSave)
     {
         AssetDatabase.SaveAssets();
     }
     return(node);
 }
Esempio n. 8
0
 public void CreateNode(Type type, Vector2 position)
 {
     AnimationBaker.StateMachine.XNode.Node node = graph.AddNode(type);
     node.position = position;
     node.name     = UnityEditor.ObjectNames.NicifyVariableName(type.Name);
     AssetDatabase.AddObjectToAsset(node, graph);
     if (NodeEditorPreferences.GetSettings().autoSave)
     {
         AssetDatabase.SaveAssets();
     }
     Repaint();
 }
Esempio n. 9
0
 public void SelectNode(AnimationBaker.StateMachine.XNode.Node node, bool add)
 {
     if (add)
     {
         List <Object> selection = new List <Object>(Selection.objects);
         selection.Add(node);
         Selection.objects = selection.ToArray();
     }
     else
     {
         Selection.objects = new Object[] { node }
     };
 }
Esempio n. 10
0
        bool IsHoveringTitle(AnimationBaker.StateMachine.XNode.Node node)
        {
            Vector2 mousePos = Event.current.mousePosition;
            //Get node position
            Vector2 nodePos = GridToWindowPosition(node.position);
            float   width   = 200;

            if (nodeSizes.ContainsKey(node))
            {
                width = nodeSizes[node].x;
            }
            Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom));

            return(windowRect.Contains(mousePos));
        }
Esempio n. 11
0
 /// <summary> Remove nodes in the graph in Selection.objects</summary>
 public void RemoveSelectedNodes()
 {
     // We need to delete reroutes starting at the highest point index to avoid shifting indices
     selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList();
     for (int i = 0; i < selectedReroutes.Count; i++)
     {
         selectedReroutes[i].RemovePoint();
     }
     selectedReroutes.Clear();
     foreach (UnityEngine.Object item in Selection.objects)
     {
         if (item is AnimationBaker.StateMachine.XNode.Node)
         {
             AnimationBaker.StateMachine.XNode.Node node = item as AnimationBaker.StateMachine.XNode.Node;
             graphEditor.RemoveNode(node);
         }
     }
 }
Esempio n. 12
0
        private void RecalculateDragOffsets(Event current)
        {
            dragOffset = new Vector2[Selection.objects.Length + selectedReroutes.Count];
            // Selected nodes
            for (int i = 0; i < Selection.objects.Length; i++)
            {
                if (Selection.objects[i] is AnimationBaker.StateMachine.XNode.Node)
                {
                    AnimationBaker.StateMachine.XNode.Node node = Selection.objects[i] as AnimationBaker.StateMachine.XNode.Node;
                    dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition);
                }
            }

            // Selected reroutes
            for (int i = 0; i < selectedReroutes.Count; i++)
            {
                dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition);
            }
        }
        /// <summary> Automatically delete Node sub-assets before deleting their script.
        /// <para/> This is important to do, because you can't delete null sub assets. </summary>
        private static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options)
        {
            // Get the object that is requested for deletion
            UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath <UnityEngine.Object>(path);

            // If we aren't deleting a script, return
            if (!(obj is UnityEditor.MonoScript))
            {
                return(AssetDeleteResult.DidNotDelete);
            }

            // Check script type. Return if deleting a non-node script
            UnityEditor.MonoScript script     = obj as UnityEditor.MonoScript;
            System.Type            scriptType = script.GetClass();
            if (scriptType == null || (scriptType != typeof(AnimationBaker.StateMachine.XNode.Node) && !scriptType.IsSubclassOf(typeof(AnimationBaker.StateMachine.XNode.Node))))
            {
                return(AssetDeleteResult.DidNotDelete);
            }

            // Find all ScriptableObjects using this script
            string[] guids = AssetDatabase.FindAssets("t:" + scriptType);
            for (int i = 0; i < guids.Length; i++)
            {
                string   assetpath = AssetDatabase.GUIDToAssetPath(guids[i]);
                Object[] objs      = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath);
                for (int k = 0; k < objs.Length; k++)
                {
                    AnimationBaker.StateMachine.XNode.Node node = objs[k] as AnimationBaker.StateMachine.XNode.Node;
                    if (node.GetType() == scriptType)
                    {
                        if (node != null && node.graph != null)
                        {
                            // Delete the node and notify the user
                            Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
                            node.graph.RemoveNode(node);
                        }
                    }
                }
            }
            // We didn't actually delete the script. Tell the internal system to carry on with normal deletion procedure
            return(AssetDeleteResult.DidNotDelete);
        }
Esempio n. 14
0
        /// <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 AnimationBaker.StateMachine.XNode.Node)
            {
                AnimationBaker.StateMachine.XNode.Node node = Selection.activeObject as AnimationBaker.StateMachine.XNode.Node;
                contextMenu.AddItem(new GUIContent("Move To Top"), false, () => MoveNodeToTop(node));
                contextMenu.AddItem(new GUIContent("Rename"), false, RenameSelectedNode);
            }

            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 AnimationBaker.StateMachine.XNode.Node)
            {
                AnimationBaker.StateMachine.XNode.Node node = Selection.activeObject as AnimationBaker.StateMachine.XNode.Node;
                AddCustomContextMenuItems(contextMenu, node);
            }

            contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
        }
Esempio n. 15
0
        private void DrawNodes()
        {
            Event e = Event.current;

            if (e.type == EventType.Layout)
            {
                selectionCache = new List <UnityEngine.Object>(Selection.objects);
            }

            //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 AnimationBaker.StateMachine.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>();

            // 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;

            if (e.type == EventType.Layout)
            {
                culledNodes = new List <AnimationBaker.StateMachine.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;
                }
                AnimationBaker.StateMachine.XNode.Node node = graph.nodes[n];

                NodeEditor nodeEditor = NodeEditor.GetEditor(node);

                // Culling
                if (e.type == EventType.Layout)
                {
                    // Cull unselected nodes outside view
                    if (!Selection.Contains(node) && ShouldBeCulled(nodeEditor))
                    {
                        culledNodes.Add(node);
                        continue;
                    }
                }
                else if (culledNodes.Contains(node))
                {
                    continue;
                }

                if (e.type == EventType.Repaint)
                {
                    _portConnectionPoints = _portConnectionPoints.Where(x => x.Key.node != node).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
                }

                NodeEditor.portPositions = new Dictionary <AnimationBaker.StateMachine.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();

                //If user changed a value, notify other scripts through onUpdateNode
                if (EditorGUI.EndChangeCheck())
                {
                    if (NodeEditor.onUpdateNode != null)
                    {
                        NodeEditor.onUpdateNode(node);
                    }
                    // EditorUtility.SetDirty(node);
                    node.IsDirty = true;
                    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);
                        if (portConnectionPoints.ContainsKey(kvp.Key))
                        {
                            portConnectionPoints[kvp.Key] = rect;
                        }
                        else
                        {
                            portConnectionPoints.Add(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 (AnimationBaker.StateMachine.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 (AnimationBaker.StateMachine.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);

            //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);
                }
            }
        }
Esempio n. 16
0
        /// <summary> Draw an editable list of instance ports. Port names are named as "[fieldName] [index]" </summary>
        /// <param name="fieldName">Supply a list for editable values</param>
        /// <param name="type">Value type of added instance ports</param>
        /// <param name="serializedObject">The serializedObject of the node</param>
        /// <param name="connectionType">Connection type of added instance ports</param>
        public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, AnimationBaker.StateMachine.XNode.Node.ConnectionType connectionType = AnimationBaker.StateMachine.XNode.Node.ConnectionType.Multiple)
        {
            AnimationBaker.StateMachine.XNode.Node node = serializedObject.targetObject as AnimationBaker.StateMachine.XNode.Node;
            SerializedProperty arrayData = serializedObject.FindProperty(fieldName);
            bool hasArrayData            = arrayData != null && arrayData.isArray;
            int  arraySize = hasArrayData ? arrayData.arraySize : 0;

            List <AnimationBaker.StateMachine.XNode.NodePort> instancePorts = node.InstancePorts.Where(x => x.fieldName.StartsWith(fieldName)).OrderBy(x => x.fieldName).ToList();

            for (int i = 0; i < instancePorts.Count(); i++)
            {
                GUILayout.BeginHorizontal();
                if (GUILayout.Button("-", GUILayout.Width(30)))
                {
                    // Clear the removed ports connections
                    instancePorts[i].ClearConnections();
                    // Move following connections one step up to replace the missing connection
                    for (int k = i + 1; k < instancePorts.Count(); k++)
                    {
                        for (int j = 0; j < instancePorts[k].ConnectionCount; j++)
                        {
                            AnimationBaker.StateMachine.XNode.NodePort other = instancePorts[k].GetConnectionPort(j);
                            instancePorts[k].DisconnectPort(other);
                            instancePorts[k - 1].Connect(other);
                        }
                    }
                    // Remove the last instance port, to avoid messing up the indexing
                    node.RemoveInstancePort(instancePorts[instancePorts.Count() - 1].fieldName);
                    serializedObject.Update();
                    // EditorUtility.SetDirty(node);
                    node.IsDirty = true;
                    if (hasArrayData)
                    {
                        arrayData.DeleteArrayElementAtIndex(i);
                        arraySize--;
                    }
                    i--;
                }
                else
                {
                    if (hasArrayData)
                    {
                        if (i < arraySize)
                        {
                            SerializedProperty itemData = arrayData.GetArrayElementAtIndex(i);
                            if (itemData != null)
                            {
                                EditorGUILayout.PropertyField(itemData, new GUIContent(ObjectNames.NicifyVariableName(fieldName) + " " + i));
                            }
                            else
                            {
                                EditorGUILayout.LabelField("[Missing array data]");
                            }
                        }
                        else
                        {
                            EditorGUILayout.LabelField("[Out of bounds]");
                        }
                    }
                    else
                    {
                        EditorGUILayout.LabelField(instancePorts[i].fieldName);
                    }
                    NodeEditorGUILayout.PortField(new GUIContent(), node.GetPort(instancePorts[i].fieldName), GUILayout.Width(-4));
                }
                GUILayout.EndHorizontal();
            }
            GUILayout.BeginHorizontal();
            EditorGUILayout.Space();
            if (GUILayout.Button("+", GUILayout.Width(30)))
            {
                string newName = fieldName + " 0";
                int    i       = 0;
                while (node.HasPort(newName))
                {
                    newName = fieldName + " " + (++i);
                }

                instancePorts.Add(node.AddInstanceOutput(type, connectionType, newName));
                serializedObject.Update();
                // EditorUtility.SetDirty(node);
                node.IsDirty = true;
                if (hasArrayData)
                {
                    arrayData.InsertArrayElementAtIndex(arraySize);
                }
            }
            GUILayout.EndHorizontal();
        }
Esempio n. 17
0
        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.IsConnectedToPort(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 AnimationBaker.StateMachine.XNode.Node)
                            {
                                AnimationBaker.StateMachine.XNode.Node node = Selection.objects[i] as AnimationBaker.StateMachine.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;
                    graph.dragPosition = panOffset;
                }
                break;

            case EventType.MouseDown:
                Repaint();
                if (e.button == 0)
                {
                    draggedOutputReroutes.Clear();

                    if (IsHoveringPort)
                    {
                        if (hoveredPort.IsOutput)
                        {
                            draggedOutput = hoveredPort;
                        }
                        else
                        {
                            hoveredPort.VerifyConnections();
                            if (hoveredPort.IsConnected)
                            {
                                AnimationBaker.StateMachine.XNode.Node     node   = hoveredPort.node;
                                AnimationBaker.StateMachine.XNode.NodePort output = hoveredPort.Connection;
                                int outputConnectionIndex = output.GetConnectionPortIndex(hoveredPort);
                                draggedOutputReroutes = output.GetReroutePoints(outputConnectionIndex);
                                hoveredPort.DisconnectPort(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 (Selection.activeObject != graph)
                            {
                                Selection.activeObject = graph;
                            }
                        }
                    }
                }
                break;

            case EventType.MouseUp:
                if (e.button == 0)
                {
                    //Port drag release
                    if (IsDraggingPort)
                    {
                        //If connection is valid, save it
                        if (draggedOutputTarget != null)
                        {
                            AnimationBaker.StateMachine.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.GetConnectionPortIndex(draggedOutputTarget);
                            if (connectionIndex != -1)
                            {
                                draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes);
                                if (NodeEditor.onUpdateNode != null)
                                {
                                    NodeEditor.onUpdateNode(node);
                                }
                                // EditorUtility.SetDirty(graph);
                                graph.IsDirty = true;
                            }
                        }
                        //Release dragged connection
                        draggedOutput       = null;
                        draggedOutputTarget = null;
                        // EditorUtility.SetDirty(graph);
                        graph.IsDirty = true;
                        if (NodeEditorPreferences.GetSettings().autoSave)
                        {
                            AssetDatabase.SaveAssets();
                        }
                    }
                    else if (currentActivity == NodeActivity.DragNode)
                    {
                        IEnumerable <AnimationBaker.StateMachine.XNode.Node> nodes = Selection.objects.Where(x => x is AnimationBaker.StateMachine.XNode.Node).Select(x => x as AnimationBaker.StateMachine.XNode.Node);
                        foreach (AnimationBaker.StateMachine.XNode.Node node in nodes)
                        {
                            node.IsDirty = true;
                        }
                        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 (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;
            }
        }