From 1b32e2ec0352f27647163c1a6f390540b94380a8 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 19:00:56 -0300 Subject: [PATCH 1/7] Added new methods at NodeEditorUtilities _ HasCompatiblePortType: Looking for ports with value Type compatible with a given type. - GetCompatibleNodesTypes: Filter only node types that contains some port value type compatible with an given type --- Scripts/Editor/NodeEditorUtilities.cs | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index ac12e33..900e2e2 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -127,6 +127,64 @@ namespace XNodeEditor { return methods.Count() > 0; } + /// + /// Looking for ports with value Type compatible with a given type. + /// + /// Node to search + /// Type to find compatiblities + /// + /// True if NodeType has some port with value type compatible + public static bool HasCompatiblePortType(Type nodeType, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) + { + Type findType = typeof(XNode.Node.InputAttribute); + if (direction == XNode.NodePort.IO.Output) + findType = typeof(XNode.Node.OutputAttribute); + + //Get All fields from node type and we go filter only field with portAttribute. + //This way is possible to know the values of the all ports and if have some with compatible value tue + foreach (FieldInfo f in XNode.NodeDataCache.GetNodeFields(nodeType)) + { + var portAttribute = f.GetCustomAttributes(findType, false).FirstOrDefault(); + if (portAttribute != null) + { + if (IsCastableTo(f.FieldType, compatibleType)) + { + return true; + } + } + } + + return false; + } + + /// + /// Filter only node types that contains some port value type compatible with an given type + /// + /// List with all nodes type to filter + /// Compatible Type to Filter + /// Return Only Node Types with ports compatible, or an empty list + public static List GetCompatibleNodesTypes(Type[] nodeTypes, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) + { + //Result List + List filteredTypes = new List(); + + //Return empty list + if (nodeTypes == null) { return filteredTypes; } + if (compatibleType == null) { return filteredTypes; } + + //Find compatiblity + foreach (Type findType in nodeTypes) + { + if (HasCompatiblePortType(findType, compatibleType, direction)) + { + filteredTypes.Add(findType); + } + } + + return filteredTypes; + } + + /// Return a prettiefied type name. public static string PrettyName(this Type type) { if (type == null) return "null"; From 2d91e45dba95cba5ede5263b5e49da78ab2cc826 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 19:12:35 -0300 Subject: [PATCH 2/7] Added parameter to AddContexMenuItens - Added parameters to get contextMenu with compatibles node filter. These parameters are auto-filled and do not interfere with the standard operation of this function. > Param: Type compatibleType: Use it to filter only nodes with ports value type, compatible with this type: Default null. > Param: NodePor.IO: Direction of the compatiblity: (Default Input) --- Scripts/Editor/NodeGraphEditor.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 8b671e2..b7ed32d 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -64,11 +64,24 @@ namespace XNodeEditor return 0; } - /// Add items for the context menu when right-clicking this node. Override to add custom menu items. - public virtual void AddContextMenuItems(GenericMenu menu) + /// + /// Add items for the context menu when right-clicking this node. + /// Override to add custom menu items. + /// + /// + /// Use it to filter only nodes with ports value type, compatible with this type + /// Direction of the compatiblity + public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); - var nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); + + Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); + + if (compatibleType != null) + { + nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); + } + for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; @@ -101,6 +114,7 @@ namespace XNodeEditor menu.AddCustomContextMenuItems(target); } + /// Returned gradient is used to color noodles /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. From 37d82ad2157ddd3122ec52d9c3e9d537f15a6c16 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 19:31:07 -0300 Subject: [PATCH 3/7] Added DragToCreate compatible filter - Added preference to filter the nodes from the draggable context menu, to show only nodes that have ports compatible with the dragged port - Minor modification in NodeEditorActions, to allow the filter - Minor modification in NodeGraphEditor to use or not the filter, based in preferences --- Scripts/Editor/NodeEditorAction.cs | 2 +- Scripts/Editor/NodeEditorPreferences.cs | 11 +++++++++++ Scripts/Editor/NodeGraphEditor.cs | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 738a602..438299e 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -290,7 +290,7 @@ namespace XNodeEditor else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { GenericMenu menu = new GenericMenu(); - graphEditor.AddContextMenuItems(menu); + graphEditor.AddContextMenuItems(menu, draggedOutput.ValueType); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } //Release dragged connection diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index d16dfd0..73f2552 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -37,6 +37,7 @@ namespace XNodeEditor { public bool autoSave = true; public bool openOnCreate = true; public bool dragToCreate = true; + public bool dragToCreateFilter = true; public bool zoomToMouse = true; public bool portTooltips = true; [SerializeField] private string typeColorsData = ""; @@ -166,6 +167,16 @@ namespace XNodeEditor { settings.noodleStroke = (NoodleStroke) EditorGUILayout.EnumPopup("Noodle stroke", (Enum) settings.noodleStroke); settings.portTooltips = EditorGUILayout.Toggle("Port Tooltips", settings.portTooltips); settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); + + //Drag to Create Filter + int oldIndent = EditorGUI.indentLevel; + EditorGUI.indentLevel = oldIndent + 1; + GUI.enabled = settings.dragToCreate; + settings.dragToCreateFilter = EditorGUILayout.Toggle(new GUIContent("Filter", "Only show nodes that are compatible with the dragged port"), settings.dragToCreateFilter); + GUI.enabled = true; + EditorGUI.indentLevel = oldIndent; + + //END if (GUI.changed) { SavePrefs(key, settings); NodeEditorWindow.RepaintAll(); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index b7ed32d..5890309 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -77,7 +77,7 @@ namespace XNodeEditor Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - if (compatibleType != null) + if (compatibleType != null && NodeEditorPreferences.GetSettings().dragToCreateFilter) { nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); } From a03c4c5d0e7e30225b48621d6fef9835172971e2 Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 20:57:40 -0300 Subject: [PATCH 4/7] Added filtered node at port contextMenu Added compatible menus when the right mouse button is clicked on the port and preferences filter is active --- Scripts/Editor/NodeEditorGUI.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 21d6d0f..eb6811f 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -118,6 +118,16 @@ namespace XNodeEditor { contextMenu.AddItem(new GUIContent($"Disconnect({name})"), false, () => hoveredPort.Disconnect(index)); } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); + //Get compatible nodes with this port + if (NodeEditorPreferences.GetSettings().dragToCreateFilter) + { + contextMenu.AddSeparator(""); + + if (hoveredPort.direction == XNode.NodePort.IO.Input) + graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, XNode.NodePort.IO.Output); + else + graphEditor.AddContextMenuItems(contextMenu, hoveredPort.ValueType, XNode.NodePort.IO.Input); + } contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } From b0f20cafa4e7c539d3cbf363c2d8bcb097904c3a Mon Sep 17 00:00:00 2001 From: juliocp Date: Wed, 7 Oct 2020 21:08:31 -0300 Subject: [PATCH 5/7] renamed dragToCreate to createFilter - Renamed the preferences dragToCreateFilter to createFilter - Removed dragToCreate as a condition to use createFilter --- Scripts/Editor/NodeEditorGUI.cs | 2 +- Scripts/Editor/NodeEditorPreferences.cs | 14 ++++---------- Scripts/Editor/NodeGraphEditor.cs | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index eb6811f..73097b4 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -119,7 +119,7 @@ namespace XNodeEditor { } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); //Get compatible nodes with this port - if (NodeEditorPreferences.GetSettings().dragToCreateFilter) + if (NodeEditorPreferences.GetSettings().createFilter) { contextMenu.AddSeparator(""); diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 73f2552..ef42e7a 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -37,7 +37,7 @@ namespace XNodeEditor { public bool autoSave = true; public bool openOnCreate = true; public bool dragToCreate = true; - public bool dragToCreateFilter = true; + public bool createFilter = true; public bool zoomToMouse = true; public bool portTooltips = true; [SerializeField] private string typeColorsData = ""; @@ -166,15 +166,9 @@ namespace XNodeEditor { settings.noodleThickness = EditorGUILayout.FloatField(new GUIContent("Noodle thickness", "Noodle Thickness of the node connections"), settings.noodleThickness); settings.noodleStroke = (NoodleStroke) EditorGUILayout.EnumPopup("Noodle stroke", (Enum) settings.noodleStroke); settings.portTooltips = EditorGUILayout.Toggle("Port Tooltips", settings.portTooltips); - settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); - - //Drag to Create Filter - int oldIndent = EditorGUI.indentLevel; - EditorGUI.indentLevel = oldIndent + 1; - GUI.enabled = settings.dragToCreate; - settings.dragToCreateFilter = EditorGUILayout.Toggle(new GUIContent("Filter", "Only show nodes that are compatible with the dragged port"), settings.dragToCreateFilter); - GUI.enabled = true; - EditorGUI.indentLevel = oldIndent; + settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); + settings.createFilter = EditorGUILayout.Toggle(new GUIContent("Create Filter", "Only show nodes that are compatible with the selected port"), settings.createFilter); + //END if (GUI.changed) { diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 5890309..dd9203c 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -77,7 +77,7 @@ namespace XNodeEditor Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - if (compatibleType != null && NodeEditorPreferences.GetSettings().dragToCreateFilter) + if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) { nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); } From fdd9b24eca32a945ef43c6e8901f869581b5990b Mon Sep 17 00:00:00 2001 From: juliocp Date: Mon, 12 Oct 2020 21:17:53 -0300 Subject: [PATCH 6/7] Added Virtual portStyle - Added the GetPortStyle(...) as virtual method at GraphEditor. With this, users can customize the texture for different ports, and they can also modify the padding without having to overwrite the default xNode style. This prevents many problems when working with multiple graphics. The style properties used from Style is: - padding-left. - padding-right. - normal.background, to border texture. - active.background, to dot texture. --- Scripts/Editor/NodeEditorAction.cs | 336 ++++++++++++++++++-------- Scripts/Editor/NodeEditorGUI.cs | 6 +- Scripts/Editor/NodeEditorGUILayout.cs | 303 +++++++++++++++-------- Scripts/Editor/NodeEditorResources.cs | 4 + Scripts/Editor/NodeGraphEditor.cs | 111 ++++++--- 5 files changed, 524 insertions(+), 236 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index e78aaec..820aae3 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -5,8 +5,10 @@ using UnityEditor; using UnityEngine; using XNodeEditor.Internal; -namespace XNodeEditor { - public partial class NodeEditorWindow { +namespace XNodeEditor +{ + public partial class NodeEditorWindow + { public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid } public static NodeActivity currentActivity = NodeActivity.Idle; public static bool isPanning { get; private set; } @@ -43,14 +45,17 @@ namespace XNodeEditor { private Vector2 lastMousePosition; private float dragThreshold = 1f; - public void Controls() { + public void Controls() + { wantsMouseMove = true; Event e = Event.current; - switch (e.type) { + switch (e.type) + { case EventType.DragUpdated: case EventType.DragPerform: DragAndDrop.visualMode = DragAndDropVisualMode.Generic; - if (e.type == EventType.DragPerform) { + if (e.type == EventType.DragPerform) + { DragAndDrop.AcceptDrag(); graphEditor.OnDropObjects(DragAndDrop.objectReferences); } @@ -66,52 +71,68 @@ namespace XNodeEditor { if (NodeEditorPreferences.GetSettings().zoomToMouse) panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset); break; case EventType.MouseDrag: - if (e.button == 0) { - if (IsDraggingPort) { + if (e.button == 0) + { + if (IsDraggingPort) + { // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously - if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) { + if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) + { draggedOutputTarget = hoveredPort; - } else { + } + else + { draggedOutputTarget = null; } Repaint(); - } else if (currentActivity == NodeActivity.HoldNode) { + } + else if (currentActivity == NodeActivity.HoldNode) + { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); } - if (currentActivity == NodeActivity.DragNode) { + 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) { + 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; Undo.RecordObject(node, "Moved Node"); Vector2 initial = node.position; node.position = mousePos + dragOffset[i]; - if (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; } // 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) { + if (offset.sqrMagnitude > 0) + { + foreach (XNode.NodePort output in node.Outputs) + { Rect rect; - if (portConnectionPoints.TryGetValue(output, out rect)) { + if (portConnectionPoints.TryGetValue(output, out rect)) + { rect.position += offset; portConnectionPoints[output] = rect; } } - foreach (XNode.NodePort input in node.Inputs) { + foreach (XNode.NodePort input in node.Inputs) + { Rect rect; - if (portConnectionPoints.TryGetValue(input, out rect)) { + if (portConnectionPoints.TryGetValue(input, out rect)) + { rect.position += offset; portConnectionPoints[input] = rect; } @@ -120,22 +141,28 @@ namespace XNodeEditor { } } // Move selected reroutes with offset - for (int i = 0; i < selectedReroutes.Count; i++) { + for (int i = 0; i < selectedReroutes.Count; i++) + { Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i]; - if (gridSnap) { + 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) { + } + else if (currentActivity == NodeActivity.HoldGrid) + { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; preBoxSelectionReroute = selectedReroutes.ToArray(); dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); - } else if (currentActivity == NodeActivity.DragGrid) { + } + 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); } @@ -143,9 +170,12 @@ namespace XNodeEditor { selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } - } else if (e.button == 1 || e.button == 2) { + } + else if (e.button == 1 || e.button == 2) + { //check drag threshold for larger screens - if (e.delta.magnitude > dragThreshold) { + if (e.delta.magnitude > dragThreshold) + { panOffset += e.delta * zoom; isPanning = true; } @@ -153,17 +183,23 @@ namespace XNodeEditor { break; case EventType.MouseDown: Repaint(); - if (e.button == 0) { + if (e.button == 0) + { draggedOutputReroutes.Clear(); - if (IsHoveringPort) { - if (hoveredPort.IsOutput) { + if (IsHoveringPort) + { + if (hoveredPort.IsOutput) + { draggedOutput = hoveredPort; autoConnectOutput = hoveredPort; - } else { + } + else + { hoveredPort.VerifyConnections(); autoConnectOutput = null; - if (hoveredPort.IsConnected) { + if (hoveredPort.IsConnected) + { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); @@ -174,25 +210,33 @@ namespace XNodeEditor { if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); } } - } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { + } + else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) + { // If mousedown on node header, select or deselect - if (!Selection.Contains(hoveredNode)) { + 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); + } + 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) { + } + else if (IsHoveringReroute) + { // If reroute isn't selected - if (!selectedReroutes.Contains(hoveredReroute)) { + if (!selectedReroutes.Contains(hoveredReroute)) + { // Add it if (e.control || e.shift) selectedReroutes.Add(hoveredReroute); // Select it - else { + else + { selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } @@ -204,9 +248,11 @@ namespace XNodeEditor { currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all - else if (!IsHoveringNode) { + else if (!IsHoveringNode) + { currentActivity = NodeActivity.HoldGrid; - if (!e.control && !e.shift) { + if (!e.control && !e.shift) + { selectedReroutes.Clear(); Selection.activeObject = null; } @@ -214,24 +260,29 @@ namespace XNodeEditor { } break; case EventType.MouseUp: - if (e.button == 0) { + if (e.button == 0) + { //Port drag release - if (IsDraggingPort) { + if (IsDraggingPort) + { // If connection is valid, save it - if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) { + if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) + { 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) { + if (connectionIndex != -1) + { draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); EditorUtility.SetDirty(graph); } } // Open context menu for auto-connection if there is no target node - else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { + else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) + { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -241,13 +292,18 @@ namespace XNodeEditor { draggedOutputTarget = null; EditorUtility.SetDirty(graph); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } else if (currentActivity == NodeActivity.DragNode) { + } + else if (currentActivity == NodeActivity.DragNode) + { IEnumerable 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) { + } + else if (!IsHoveringNode) + { // If click outside node, release field focus - if (!isPanning) { + if (!isPanning) + { EditorGUI.FocusTextInControl(null); EditorGUIUtility.editingTextField = false; } @@ -255,44 +311,61 @@ namespace XNodeEditor { } // If click node header, select it. - if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { + if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) + { selectedReroutes.Clear(); SelectNode(hoveredNode, false); // Double click to center node - if (isDoubleClick) { + 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)) { + if (IsHoveringReroute && !(e.control || e.shift)) + { selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } Repaint(); currentActivity = NodeActivity.Idle; - } else if (e.button == 1 || e.button == 2) { - if (!isPanning) { - if (IsDraggingPort) { + } + 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) { + } + 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) { + } + else if (IsHoveringReroute) + { ShowRerouteContextMenu(hoveredReroute); - } else if (IsHoveringPort) { + } + else if (IsHoveringPort) + { ShowPortContextMenu(hoveredPort); - } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { + } + 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) { + } + else if (!IsHoveringNode) + { autoConnectOutput = null; GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); @@ -307,18 +380,27 @@ namespace XNodeEditor { case EventType.KeyDown: if (EditorGUIUtility.editingTextField) break; else if (e.keyCode == KeyCode.F) Home(); - if (NodeEditorUtilities.IsMac()) { + if (NodeEditorUtilities.IsMac()) + { if (e.keyCode == KeyCode.Return) RenameSelectedNode(); - } else { + } + 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) { + 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) { + } + else + { + foreach (XNode.Node node in graph.nodes) + { SelectNode(node, true); } } @@ -327,19 +409,28 @@ namespace XNodeEditor { break; case EventType.ValidateCommand: case EventType.ExecuteCommand: - if (e.commandName == "SoftDelete") { + if (e.commandName == "SoftDelete") + { if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes(); e.Use(); - } else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") { + } + else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") + { if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes(); e.Use(); - } else if (e.commandName == "Duplicate") { + } + else if (e.commandName == "Duplicate") + { if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes(); e.Use(); - } else if (e.commandName == "Copy") { + } + else if (e.commandName == "Copy") + { if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); e.Use(); - } else if (e.commandName == "Paste") { + } + else if (e.commandName == "Paste") + { if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); e.Use(); } @@ -347,7 +438,8 @@ namespace XNodeEditor { break; case EventType.Ignore: // If release mouse outside window - if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { + if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) + { Repaint(); currentActivity = NodeActivity.Idle; } @@ -355,45 +447,57 @@ namespace XNodeEditor { } } - private void RecalculateDragOffsets(Event current) { + 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 XNode.Node) { + 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; dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition); } } // Selected reroutes - for (int i = 0; i < selectedReroutes.Count; i++) { + for (int i = 0; i < selectedReroutes.Count; i++) + { dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition); } } /// Puts all selected nodes in focus. If no nodes are present, resets view and zoom to to origin - public void Home() { + public void Home() + { var nodes = Selection.objects.Where(o => o is XNode.Node).Cast().ToList(); - if (nodes.Count > 0) { + if (nodes.Count > 0) + { Vector2 minPos = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); Vector2 maxPos = nodes.Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero)).Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y))); panOffset = -(minPos + (maxPos - minPos) / 2f); - } else { + } + else + { zoom = 2; panOffset = Vector2.zero; } } /// Remove nodes in the graph in Selection.objects - public void RemoveSelectedNodes() { + 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++) { + for (int i = 0; i < selectedReroutes.Count; i++) + { selectedReroutes[i].RemovePoint(); } selectedReroutes.Clear(); - foreach (UnityEngine.Object item in Selection.objects) { - if (item is XNode.Node) { + foreach (UnityEngine.Object item in Selection.objects) + { + if (item is XNode.Node) + { XNode.Node node = item as XNode.Node; graphEditor.RemoveNode(node); } @@ -401,29 +505,37 @@ namespace XNodeEditor { } /// Initiate a rename on the currently selected node - public void RenameSelectedNode() { - if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { + public void RenameSelectedNode() + { + if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) + { XNode.Node node = Selection.activeObject as XNode.Node; Vector2 size; - if (nodeSizes.TryGetValue(node, out size)) { + if (nodeSizes.TryGetValue(node, out size)) + { RenamePopup.Show(Selection.activeObject, size.x); - } else { + } + else + { RenamePopup.Show(Selection.activeObject); } } } /// Draw this node on top of other nodes by placing it last in the graph.nodes list - public void MoveNodeToTop(XNode.Node node) { + public void MoveNodeToTop(XNode.Node node) + { int index; - while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { + while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) + { graph.nodes[index] = graph.nodes[index + 1]; graph.nodes[index + 1] = node; } } /// Duplicate selected nodes and select the duplicates - public void DuplicateSelectedNodes() { + public void DuplicateSelectedNodes() + { // Get selected nodes which are part of this graph XNode.Node[] selectedNodes = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); if (selectedNodes == null || selectedNodes.Length == 0) return; @@ -432,15 +544,18 @@ namespace XNodeEditor { InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30)); } - public void CopySelectedNodes() { + public void CopySelectedNodes() + { copyBuffer = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); } - public void PasteNodes(Vector2 pos) { + public void PasteNodes(Vector2 pos) + { InsertDuplicateNodes(copyBuffer, pos); } - private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) { + private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) + { if (nodes == null || nodes.Length == 0) return; // Get top-left node @@ -449,14 +564,16 @@ namespace XNodeEditor { UnityEngine.Object[] newNodes = new UnityEngine.Object[nodes.Length]; Dictionary substitutes = new Dictionary(); - for (int i = 0; i < nodes.Length; i++) { + for (int i = 0; i < nodes.Length; i++) + { XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; Type nodeType = srcNode.GetType(); - if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) { + if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) + { int typeCount = graph.nodes.Count(x => x.GetType() == nodeType); if (typeCount >= disallowAttrib.max) continue; } @@ -468,16 +585,20 @@ namespace XNodeEditor { } // Walk through the selected nodes again, recreate connections, using the new nodes - for (int i = 0; i < nodes.Length; i++) { + for (int i = 0; i < nodes.Length; i++) + { XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; - foreach (XNode.NodePort port in srcNode.Ports) { - for (int c = 0; c < port.ConnectionCount; c++) { + foreach (XNode.NodePort port in srcNode.Ports) + { + for (int c = 0; c < port.ConnectionCount; c++) + { XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c); XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c); XNode.Node newNodeIn, newNodeOut; - if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) { + if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) + { newNodeIn.UpdatePorts(); newNodeOut.UpdatePorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); @@ -493,8 +614,10 @@ namespace XNodeEditor { } /// Draw a connection as we are dragging it - public void DrawDraggedConnection() { - if (IsDraggingPort) { + public void DrawDraggedConnection() + { + if (IsDraggingPort) + { Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); float thickness = graphEditor.GetNoodleThickness(draggedOutput, null); NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null); @@ -504,7 +627,8 @@ namespace XNodeEditor { if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return; List gridPoints = new List(); gridPoints.Add(fromRect.center); - for (int i = 0; i < draggedOutputReroutes.Count; i++) { + for (int i = 0; i < draggedOutputReroutes.Count; i++) + { gridPoints.Add(draggedOutputReroutes[i]); } if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center); @@ -512,24 +636,27 @@ namespace XNodeEditor { DrawNoodle(gradient, path, stroke, thickness, gridPoints); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(draggedOutput); Color bgcol = Color.black; Color frcol = gradient.colorKeys[0].color; bgcol.a = 0.6f; frcol.a = 0.6f; // Loop through reroute points again and draw the points - for (int i = 0; i < draggedOutputReroutes.Count; i++) { + for (int i = 0; i < draggedOutputReroutes.Count; i++) + { // Draw reroute point at position Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16)); rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8); rect = GridToWindowRect(rect); - NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol); + NodeEditorGUILayout.DrawPortHandle(rect, bgcol, frcol, portStyle.normal.background, portStyle.active.background); } } } - bool IsHoveringTitle(XNode.Node node) { + bool IsHoveringTitle(XNode.Node node) + { Vector2 mousePos = Event.current.mousePosition; //Get node position Vector2 nodePos = GridToWindowPosition(node.position); @@ -542,7 +669,8 @@ namespace XNodeEditor { } /// Attempt to connect dragged output to target node - public void AutoConnect(XNode.Node node) { + public void AutoConnect(XNode.Node node) + { if (autoConnectOutput == null) return; // Find input port of same type diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index d914eff..379dc5e 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -334,6 +334,8 @@ namespace XNodeEditor { if (!_portConnectionPoints.TryGetValue(output, out fromRect)) continue; Color portColor = graphEditor.GetPortColor(output); + GUIStyle portStyle = graphEditor.GetPortStyle(output); + for (int k = 0; k < output.ConnectionCount; k++) { XNode.NodePort input = output.GetConnection(k); @@ -367,11 +369,11 @@ namespace XNodeEditor { // Draw selected reroute points with an outline if (selectedReroutes.Contains(rerouteRef)) { GUI.color = NodeEditorPreferences.GetSettings().highlightColor; - GUI.DrawTexture(rect, NodeEditorResources.dotOuter); + GUI.DrawTexture(rect, portStyle.normal.background); } GUI.color = portColor; - GUI.DrawTexture(rect, NodeEditorResources.dot); + GUI.DrawTexture(rect, portStyle.active.background); if (rect.Overlaps(selectionBox)) selection.Add(rerouteRef); if (rect.Contains(mousePos)) hoveredReroute = rerouteRef; diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 1b74a32..9a6cc1b 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -7,20 +7,24 @@ using UnityEditor; using UnityEditorInternal; using UnityEngine; -namespace XNodeEditor { +namespace XNodeEditor +{ /// xNode-specific version of - public static class NodeEditorGUILayout { + public static class NodeEditorGUILayout + { private static readonly Dictionary> reorderableListCache = new Dictionary>(); private static int reorderableListIndex = -1; /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) { - PropertyField(property, (GUIContent) null, includeChildren, options); + public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) + { + PropertyField(property, (GUIContent)null, includeChildren, options); } /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) { + public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) + { if (property == null) throw new NullReferenceException(); XNode.Node node = property.serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(property.name); @@ -28,28 +32,33 @@ namespace XNodeEditor { } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { + public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) + { PropertyField(property, null, port, includeChildren, options); } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { + 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 { + else + { Rect rect = new Rect(); List 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) { + 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)) { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) + { dynamicPortList = inputAttribute.dynamicPortList; showBacking = inputAttribute.backingValue; } @@ -60,30 +69,40 @@ namespace XNodeEditor { float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) { - if (attr is SpaceAttribute) { + 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) { + } + 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; - } else if (attr is TooltipAttribute) { + } + else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } + else if (attr is TooltipAttribute) + { tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) { + 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) { + 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, tooltip)); @@ -101,15 +120,18 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.left; rect.position = rect.position - new Vector2(16 + paddingLeft, -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) { + } + 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)) { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) + { dynamicPortList = outputAttribute.dynamicPortList; showBacking = outputAttribute.backingValue; } @@ -120,30 +142,40 @@ namespace XNodeEditor { float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) { - if (attr is SpaceAttribute) { + 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) { + } + 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; - } else if (attr is TooltipAttribute) { + } + else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } + else if (attr is TooltipAttribute) + { tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) { + 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) { + 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, tooltip), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); @@ -161,7 +193,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.width += NodeEditorResources.styles.outputPort.padding.right; + rect.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; rect.position = rect.position + new Vector2(rect.width, spacePadding); } @@ -169,7 +201,8 @@ namespace XNodeEditor { Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); - DrawPortHandle(rect, backgroundColor, col); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(port); + DrawPortHandle(rect, backgroundColor, col, portStyle.normal.background, portStyle.active.background); // Register the handle position Vector2 portPos = rect.center; @@ -177,54 +210,62 @@ namespace XNodeEditor { } } - private static System.Type GetType(SerializedProperty property) { + private static System.Type GetType(SerializedProperty property) + { System.Type parentType = property.serializedObject.targetObject.GetType(); System.Reflection.FieldInfo fi = parentType.GetFieldInfo(property.name); return fi.FieldType; } /// Make a simple port field. - public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) { + public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) + { PortField(null, port, options); } /// Make a simple port field. - public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) { + public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) + { if (port == null) return; if (options == null) options = new GUILayoutOption[] { GUILayout.MinWidth(30) }; Vector2 position = Vector3.zero; GUIContent content = label != null ? label : new GUIContent(ObjectNames.NicifyVariableName(port.fieldName)); // 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) { + if (port.direction == XNode.NodePort.IO.Input) + { // Display a label EditorGUILayout.LabelField(content, options); Rect rect = GUILayoutUtility.GetLastRect(); - float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.left; position = rect.position - new Vector2(16 + paddingLeft, 0); } // 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) { + else if (port.direction == XNode.NodePort.IO.Output) + { // Display a label EditorGUILayout.LabelField(content, NodeEditorResources.OutputPort, options); Rect rect = GUILayoutUtility.GetLastRect(); - rect.width += NodeEditorResources.styles.outputPort.padding.right; + rect.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; position = rect.position + new Vector2(rect.width, 0); } PortField(position, port); } /// Make a simple port field. - public static void PortField(Vector2 position, XNode.NodePort port) { + public static void PortField(Vector2 position, XNode.NodePort port) + { if (port == null) return; Rect rect = new Rect(position, new Vector2(16, 16)); Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); - DrawPortHandle(rect, backgroundColor, col); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(port); + + DrawPortHandle(rect, backgroundColor, col, portStyle.normal.background, portStyle.active.background); // Register the handle position Vector2 portPos = rect.center; @@ -232,19 +273,23 @@ namespace XNodeEditor { } /// Add a port field to previous layout element. - public static void AddPortField(XNode.NodePort port) { + public static void AddPortField(XNode.NodePort port) + { if (port == null) return; 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 == XNode.NodePort.IO.Input) { + if (port.direction == XNode.NodePort.IO.Input) + { rect = GUILayoutUtility.GetLastRect(); - float paddingLeft = NodeEditorResources.styles.inputPort.padding.left; + float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.left; rect.position = rect.position - new Vector2(16 + paddingLeft, 0); // 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) { + } + else if (port.direction == XNode.NodePort.IO.Output) + { rect = GUILayoutUtility.GetLastRect(); - rect.width += NodeEditorResources.styles.outputPort.padding.right; + rect.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; rect.position = rect.position + new Vector2(rect.width, 0); } @@ -252,7 +297,9 @@ namespace XNodeEditor { Color backgroundColor = NodeEditorWindow.current.graphEditor.GetPortBackgroundColor(port); Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); - DrawPortHandle(rect, backgroundColor, col); + GUIStyle portStyle = NodeEditorWindow.current.graphEditor.GetPortStyle(port); + + DrawPortHandle(rect, backgroundColor, col, portStyle.normal.background, portStyle.active.background); // Register the handle position Vector2 portPos = rect.center; @@ -260,40 +307,55 @@ namespace XNodeEditor { } /// Draws an input and an output port on the same line - public static void PortPair(XNode.NodePort input, XNode.NodePort output) { + public static void PortPair(XNode.NodePort input, XNode.NodePort output) + { GUILayout.BeginHorizontal(); NodeEditorGUILayout.PortField(input, GUILayout.MinWidth(0)); NodeEditorGUILayout.PortField(output, GUILayout.MinWidth(0)); GUILayout.EndHorizontal(); } - public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor) { + /// + /// Draw the port + /// + /// position and size + /// color for background texture of the port. Normaly used to Border + /// + /// texture for border of the dot port + /// texture for the dot port + public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor, Texture2D border, Texture2D dot) + { Color col = GUI.color; GUI.color = backgroundColor; - GUI.DrawTexture(rect, NodeEditorResources.dotOuter); + GUI.DrawTexture(rect, border); GUI.color = typeColor; - GUI.DrawTexture(rect, NodeEditorResources.dot); + GUI.DrawTexture(rect, dot); GUI.color = col; } -#region Obsolete + + #region Obsolete [Obsolete("Use IsDynamicPortListPort instead")] - public static bool IsInstancePortListPort(XNode.NodePort port) { + public static bool IsInstancePortListPort(XNode.NodePort port) + { return IsDynamicPortListPort(port); } [Obsolete("Use DynamicPortList instead")] - public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { + public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) + { DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation); } -#endregion + #endregion /// Is this port part of a DynamicPortList? - public static bool IsDynamicPortListPort(XNode.NodePort port) { + public static bool IsDynamicPortListPort(XNode.NodePort port) + { string[] parts = port.fieldName.Split(' '); if (parts.Length != 2) return false; Dictionary cache; - if (reorderableListCache.TryGetValue(port.node, out cache)) { + if (reorderableListCache.TryGetValue(port.node, out cache)) + { ReorderableList list; if (cache.TryGetValue(parts[0], out list)) return true; } @@ -306,18 +368,22 @@ namespace XNodeEditor { /// The serializedObject of the node /// Connection type of added dynamic ports /// Called on the list on creation. Use this if you want to customize the created ReorderableList - public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { + public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) + { XNode.Node node = serializedObject.targetObject as XNode.Node; - var indexedPorts = node.DynamicPorts.Select(x => { + var indexedPorts = node.DynamicPorts.Select(x => + { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) { + if (split != null && split.Length == 2 && split[0] == fieldName) + { int i = -1; - if (int.TryParse(split[1], out i)) { + if (int.TryParse(split[1], out i)) + { return new { index = i, port = x }; } } - return new { index = -1, port = (XNode.NodePort) null }; + return new { index = -1, port = (XNode.NodePort)null }; }).Where(x => x.port != null); List dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList(); @@ -325,11 +391,13 @@ namespace XNodeEditor { ReorderableList list = null; Dictionary rlc; - if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) { + if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) + { if (!rlc.TryGetValue(fieldName, out list)) list = null; } // If a ReorderableList isn't cached for this array, do so. - if (list == null) { + if (list == null) + { SerializedProperty arrayData = serializedObject.FindProperty(fieldName); list = CreateReorderableList(fieldName, dynamicPorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, onCreation); if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) rlc.Add(fieldName, list); @@ -340,53 +408,67 @@ namespace XNodeEditor { } - private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) { + private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) + { bool hasArrayData = arrayData != null && arrayData.isArray; XNode.Node node = serializedObject.targetObject as XNode.Node; ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true); string label = arrayData != null ? arrayData.displayName : ObjectNames.NicifyVariableName(fieldName); list.drawElementCallback = - (Rect rect, int index, bool isActive, bool isFocused) => { + (Rect rect, int index, bool isActive, bool isFocused) => + { XNode.NodePort port = node.GetPort(fieldName + " " + index); - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { - if (arrayData.arraySize <= index) { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) + { + if (arrayData.arraySize <= index) + { EditorGUI.LabelField(rect, "Array[" + index + "] data out of range"); return; } SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); EditorGUI.PropertyField(rect, itemData, true); - } else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); - if (port != null) { + } + else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); + if (port != null) + { Vector2 pos = rect.position + (port.IsOutput ? new Vector2(rect.width + 6, 0) : new Vector2(-36, 0)); NodeEditorGUILayout.PortField(pos, port); } }; list.elementHeightCallback = - (int index) => { - if (hasArrayData) { + (int index) => + { + if (hasArrayData) + { if (arrayData.arraySize <= index) return EditorGUIUtility.singleLineHeight; SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); return EditorGUI.GetPropertyHeight(itemData); - } else return EditorGUIUtility.singleLineHeight; + } + else return EditorGUIUtility.singleLineHeight; }; list.drawHeaderCallback = - (Rect rect) => { + (Rect rect) => + { EditorGUI.LabelField(rect, label); }; list.onSelectCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { reorderableListIndex = rl.index; }; list.onReorderCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { bool hasRect = false; bool hasNewRect = false; Rect rect = Rect.zero; Rect newRect = Rect.zero; // Move up - if (rl.index > reorderableListIndex) { - for (int i = reorderableListIndex; i < rl.index; ++i) { + if (rl.index > reorderableListIndex) + { + for (int i = reorderableListIndex; i < rl.index; ++i) + { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i + 1)); port.SwapConnections(nextPort); @@ -399,8 +481,10 @@ namespace XNodeEditor { } } // Move down - else { - for (int i = reorderableListIndex; i > rl.index; --i) { + else + { + for (int i = reorderableListIndex; i > rl.index; --i) + { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i - 1)); port.SwapConnections(nextPort); @@ -417,7 +501,8 @@ namespace XNodeEditor { serializedObject.Update(); // Move array data if there is any - if (hasArrayData) { + if (hasArrayData) + { arrayData.MoveArrayElement(reorderableListIndex, rl.index); } @@ -428,7 +513,8 @@ namespace XNodeEditor { EditorApplication.delayCall += NodeEditorWindow.current.Repaint; }; list.onAddCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { // Add dynamic port postfixed with an index number string newName = fieldName + " 0"; int i = 0; @@ -438,39 +524,51 @@ namespace XNodeEditor { else node.AddDynamicInput(type, connectionType, typeConstraint, newName); serializedObject.Update(); EditorUtility.SetDirty(node); - if (hasArrayData) { + if (hasArrayData) + { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); }; list.onRemoveCallback = - (ReorderableList rl) => { + (ReorderableList rl) => + { - var indexedPorts = node.DynamicPorts.Select(x => { + var indexedPorts = node.DynamicPorts.Select(x => + { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) { + if (split != null && split.Length == 2 && split[0] == fieldName) + { int i = -1; - if (int.TryParse(split[1], out i)) { + if (int.TryParse(split[1], out i)) + { return new { index = i, port = x }; } } - return new { index = -1, port = (XNode.NodePort) null }; + return new { index = -1, port = (XNode.NodePort)null }; }).Where(x => x.port != null); dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList(); int index = rl.index; - if (dynamicPorts[index] == null) { + if (dynamicPorts[index] == null) + { Debug.LogWarning("No port found at index " + index + " - Skipped"); - } else if (dynamicPorts.Count <= index) { + } + else if (dynamicPorts.Count <= index) + { Debug.LogWarning("DynamicPorts[" + index + "] out of range. Length was " + dynamicPorts.Count + " - Skipped"); - } else { + } + else + { // Clear the removed ports connections dynamicPorts[index].ClearConnections(); // Move following connections one step up to replace the missing connection - for (int k = index + 1; k < dynamicPorts.Count(); k++) { - for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) { + for (int k = index + 1; k < dynamicPorts.Count(); k++) + { + for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) + { XNode.NodePort other = dynamicPorts[k].GetConnection(j); dynamicPorts[k].Disconnect(other); dynamicPorts[k - 1].Connect(other); @@ -482,16 +580,20 @@ namespace XNodeEditor { EditorUtility.SetDirty(node); } - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { - if (arrayData.arraySize <= index) { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) + { + if (arrayData.arraySize <= index) + { Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped"); Debug.Log(rl.list[0]); return; } arrayData.DeleteArrayElementAtIndex(index); // Error handling. If the following happens too often, file a bug report at https://github.com/Siccity/xNode/issues - if (dynamicPorts.Count <= arrayData.arraySize) { - while (dynamicPorts.Count <= arrayData.arraySize) { + if (dynamicPorts.Count <= arrayData.arraySize) + { + while (dynamicPorts.Count <= arrayData.arraySize) + { arrayData.DeleteArrayElementAtIndex(arrayData.arraySize - 1); } UnityEngine.Debug.LogWarning("Array size exceeded dynamic ports size. Excess items removed."); @@ -501,9 +603,11 @@ namespace XNodeEditor { } }; - if (hasArrayData) { + if (hasArrayData) + { int dynamicPortCount = dynamicPorts.Count; - while (dynamicPortCount < arrayData.arraySize) { + while (dynamicPortCount < arrayData.arraySize) + { // Add dynamic port postfixed with an index number string newName = arrayData.name + " 0"; int i = 0; @@ -513,7 +617,8 @@ namespace XNodeEditor { EditorUtility.SetDirty(node); dynamicPortCount++; } - while (arrayData.arraySize < dynamicPortCount) { + while (arrayData.arraySize < dynamicPortCount) + { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index 9a5cc1e..26a79ce 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -27,10 +27,14 @@ namespace XNodeEditor { inputPort = new GUIStyle(baseStyle); inputPort.alignment = TextAnchor.UpperLeft; inputPort.padding.left = 0; + inputPort.active.background = dot; + inputPort.normal.background = dotOuter; outputPort = new GUIStyle(baseStyle); outputPort.alignment = TextAnchor.UpperRight; outputPort.padding.right = 0; + outputPort.active.background = dot; + outputPort.normal.background = dotOuter; nodeHeader = new GUIStyle(); nodeHeader.alignment = TextAnchor.MiddleCenter; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index dcc92a9..1d248f5 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -4,10 +4,12 @@ using System.Linq; using UnityEditor; using UnityEngine; -namespace XNodeEditor { +namespace XNodeEditor +{ /// Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. [CustomNodeGraphEditor(typeof(XNode.NodeGraph))] - public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase { + public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase + { [Obsolete("Use window.position instead")] public Rect position { get { return window.position; } set { window.position = value; } } /// Are we currently renaming a node? @@ -24,21 +26,25 @@ namespace XNodeEditor { /// Called when NodeEditorWindow loses focus public virtual void OnWindowFocusLost() { } - public virtual Texture2D GetGridTexture() { + public virtual Texture2D GetGridTexture() + { return NodeEditorPreferences.GetSettings().gridTexture; } - public virtual Texture2D GetSecondaryGridTexture() { + public virtual Texture2D GetSecondaryGridTexture() + { return NodeEditorPreferences.GetSettings().crossTexture; } /// Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. - public virtual NodeEditorPreferences.Settings GetDefaultPreferences() { + public virtual NodeEditorPreferences.Settings GetDefaultPreferences() + { return new NodeEditorPreferences.Settings(); } /// Returns context node menu path. Null or empty strings for hidden nodes. - public virtual string GetNodeMenuName(Type type) { + public virtual string GetNodeMenuName(Type type) + { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -48,7 +54,8 @@ namespace XNodeEditor { } /// The order by which the menu items are displayed. - public virtual int GetNodeMenuOrder(Type type) { + public virtual int GetNodeMenuOrder(Type type) + { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -71,14 +78,16 @@ namespace XNodeEditor { // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; bool disallowed = false; - if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) { + if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) + { int typeCount = target.nodes.Count(x => x.GetType() == type); if (typeCount >= disallowAttrib.max) disallowed = true; } // Add node entry to context menu if (disallowed) menu.AddItem(new GUIContent(path), false, null); - else menu.AddItem(new GUIContent(path), false, () => { + else menu.AddItem(new GUIContent(path), false, () => + { XNode.Node node = CreateNode(type, pos); NodeEditorWindow.current.AutoConnect(node); }); @@ -93,11 +102,13 @@ namespace XNodeEditor { /// Returned gradient is used to color noodles /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) { + public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) + { Gradient grad = new Gradient(); // If dragging the noodle, draw solid, slightly transparent - if (input == null) { + if (input == null) + { Color a = GetTypeColor(output.ValueType); grad.SetKeys( new GradientColorKey[] { new GradientColorKey(a, 0f) }, @@ -105,11 +116,13 @@ namespace XNodeEditor { ); } // If normal, draw gradient fading from one input color to the other - else { + else + { Color a = GetTypeColor(output.ValueType); Color b = GetTypeColor(input.ValueType); // If any port is hovered, tint white - if (window.hoveredPort == output || window.hoveredPort == input) { + if (window.hoveredPort == output || window.hoveredPort == input) + { a = Color.Lerp(a, Color.white, 0.8f); b = Color.Lerp(b, Color.white, 0.8f); } @@ -124,40 +137,66 @@ namespace XNodeEditor { /// Returned float is used for noodle thickness /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) { + public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) + { return NodeEditorPreferences.GetSettings().noodleThickness; } - public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) { + public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) + { return NodeEditorPreferences.GetSettings().noodlePath; } - public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) { + public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) + { return NodeEditorPreferences.GetSettings().noodleStroke; } /// Returned color is used to color ports - public virtual Color GetPortColor(XNode.NodePort port) { + public virtual Color GetPortColor(XNode.NodePort port) + { return GetTypeColor(port.ValueType); } + /// + /// The returned Style is used to configure the paddings and icon texture of the ports. + /// Use these properties to customize your port style. + /// + /// The properties used is: + /// [Left and Right], [Background] = border texture, + /// and [Background] = dot texture; + /// + /// the owner of the style + /// + public virtual GUIStyle GetPortStyle(XNode.NodePort port) + { + if (port.direction == XNode.NodePort.IO.Input) + return NodeEditorResources.styles.inputPort; + + return NodeEditorResources.styles.outputPort; + } + /// The returned color is used to color the background of the door. /// Usually used for outer edge effect - public virtual Color GetPortBackgroundColor(XNode.NodePort port) { + public virtual Color GetPortBackgroundColor(XNode.NodePort port) + { return Color.gray; } /// Returns generated color for a type. This color is editable in preferences - public virtual Color GetTypeColor(Type type) { + public virtual Color GetTypeColor(Type type) + { return NodeEditorPreferences.GetTypeColor(type); } /// Override to display custom tooltips - public virtual string GetPortTooltip(XNode.NodePort port) { + public virtual string GetPortTooltip(XNode.NodePort port) + { Type portType = port.ValueType; string tooltip = ""; tooltip = portType.PrettyName(); - if (port.IsOutput) { + if (port.IsOutput) + { object obj = port.node.GetValue(port); tooltip += " = " + (obj != null ? obj.ToString() : "null"); } @@ -165,12 +204,14 @@ namespace XNodeEditor { } /// Deal with objects dropped into the graph through DragAndDrop - public virtual void OnDropObjects(UnityEngine.Object[] objects) { + public virtual void OnDropObjects(UnityEngine.Object[] objects) + { if (GetType() != typeof(NodeGraphEditor)) Debug.Log("No OnDropObjects override defined for " + GetType()); } /// Create a node and save it in the graph asset - public virtual XNode.Node CreateNode(Type type, Vector2 position) { + public virtual XNode.Node CreateNode(Type type, Vector2 position) + { Undo.RecordObject(target, "Create Node"); XNode.Node node = target.AddNode(type); Undo.RegisterCreatedObjectUndo(node, "Create Node"); @@ -183,7 +224,8 @@ namespace XNodeEditor { } /// Creates a copy of the original node in the graph - public virtual XNode.Node CopyNode(XNode.Node original) { + public virtual XNode.Node CopyNode(XNode.Node original) + { Undo.RecordObject(target, "Duplicate Node"); XNode.Node node = target.CopyNode(original); Undo.RegisterCreatedObjectUndo(node, "Duplicate Node"); @@ -194,13 +236,16 @@ namespace XNodeEditor { } /// Return false for nodes that can't be removed - public virtual bool CanRemove(XNode.Node node) { + public virtual bool CanRemove(XNode.Node node) + { // Check graph attributes to see if this node is required Type graphType = target.GetType(); XNode.NodeGraph.RequireNodeAttribute[] attribs = Array.ConvertAll( graphType.GetCustomAttributes(typeof(XNode.NodeGraph.RequireNodeAttribute), true), x => x as XNode.NodeGraph.RequireNodeAttribute); - if (attribs.Any(x => x.Requires(node.GetType()))) { - if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) { + if (attribs.Any(x => x.Requires(node.GetType()))) + { + if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) + { return false; } } @@ -208,7 +253,8 @@ namespace XNodeEditor { } /// Safely remove a node and all its connections. - public virtual void RemoveNode(XNode.Node node) { + public virtual void RemoveNode(XNode.Node node) + { if (!CanRemove(node)) return; // Remove the node @@ -224,18 +270,21 @@ namespace XNodeEditor { [AttributeUsage(AttributeTargets.Class)] public class CustomNodeGraphEditorAttribute : Attribute, - XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { + XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib + { private Type inspectedType; public string editorPrefsKey; /// Tells a NodeGraphEditor which Graph type it is an editor for /// Type that this editor can edit /// Define unique key for unique layout settings instance - public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") { + public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") + { this.inspectedType = inspectedType; this.editorPrefsKey = editorPrefsKey; } - public Type GetInspectedType() { + public Type GetInspectedType() + { return inspectedType; } } From 822b7ae156485bc25a8baebca26fe1039aab221d Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 17 Dec 2020 13:43:39 +0100 Subject: [PATCH 7/7] Formatting --- Scripts/Editor/NodeEditorAction.cs | 333 ++++++++---------------- Scripts/Editor/NodeEditorGUI.cs | 6 +- Scripts/Editor/NodeEditorGUILayout.cs | 257 ++++++------------ Scripts/Editor/NodeEditorPreferences.cs | 5 +- Scripts/Editor/NodeEditorUtilities.cs | 21 +- Scripts/Editor/NodeGraphEditor.cs | 108 +++----- 6 files changed, 233 insertions(+), 497 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 85b0417..a2b95b7 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -5,10 +5,8 @@ using UnityEditor; using UnityEngine; using XNodeEditor.Internal; -namespace XNodeEditor -{ - public partial class NodeEditorWindow - { +namespace XNodeEditor { + public partial class NodeEditorWindow { public enum NodeActivity { Idle, HoldNode, DragNode, HoldGrid, DragGrid } public static NodeActivity currentActivity = NodeActivity.Idle; public static bool isPanning { get; private set; } @@ -45,17 +43,14 @@ namespace XNodeEditor private Vector2 lastMousePosition; private float dragThreshold = 1f; - public void Controls() - { + public void Controls() { wantsMouseMove = true; Event e = Event.current; - switch (e.type) - { + switch (e.type) { case EventType.DragUpdated: case EventType.DragPerform: DragAndDrop.visualMode = DragAndDropVisualMode.Generic; - if (e.type == EventType.DragPerform) - { + if (e.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); graphEditor.OnDropObjects(DragAndDrop.objectReferences); } @@ -71,68 +66,52 @@ namespace XNodeEditor if (NodeEditorPreferences.GetSettings().zoomToMouse) panOffset += (1 - oldZoom / zoom) * (WindowToGridPosition(e.mousePosition) + panOffset); break; case EventType.MouseDrag: - if (e.button == 0) - { - if (IsDraggingPort) - { + if (e.button == 0) { + if (IsDraggingPort) { // Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously - if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) - { + if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) { draggedOutputTarget = hoveredPort; - } - else - { + } else { draggedOutputTarget = null; } Repaint(); - } - else if (currentActivity == NodeActivity.HoldNode) - { + } else if (currentActivity == NodeActivity.HoldNode) { RecalculateDragOffsets(e); currentActivity = NodeActivity.DragNode; Repaint(); } - if (currentActivity == NodeActivity.DragNode) - { + 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) - { + 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; Undo.RecordObject(node, "Moved Node"); Vector2 initial = node.position; node.position = mousePos + dragOffset[i]; - if (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; } // 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) - { + if (offset.sqrMagnitude > 0) { + foreach (XNode.NodePort output in node.Outputs) { Rect rect; - if (portConnectionPoints.TryGetValue(output, out rect)) - { + if (portConnectionPoints.TryGetValue(output, out rect)) { rect.position += offset; portConnectionPoints[output] = rect; } } - foreach (XNode.NodePort input in node.Inputs) - { + foreach (XNode.NodePort input in node.Inputs) { Rect rect; - if (portConnectionPoints.TryGetValue(input, out rect)) - { + if (portConnectionPoints.TryGetValue(input, out rect)) { rect.position += offset; portConnectionPoints[input] = rect; } @@ -141,28 +120,22 @@ namespace XNodeEditor } } // Move selected reroutes with offset - for (int i = 0; i < selectedReroutes.Count; i++) - { + for (int i = 0; i < selectedReroutes.Count; i++) { Vector2 pos = mousePos + dragOffset[Selection.objects.Length + i]; - if (gridSnap) - { + 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) - { + } else if (currentActivity == NodeActivity.HoldGrid) { currentActivity = NodeActivity.DragGrid; preBoxSelection = Selection.objects; preBoxSelectionReroute = selectedReroutes.ToArray(); dragBoxStart = WindowToGridPosition(e.mousePosition); Repaint(); - } - else if (currentActivity == NodeActivity.DragGrid) - { + } 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); } @@ -170,12 +143,9 @@ namespace XNodeEditor selectionBox = new Rect(boxStartPos, boxSize); Repaint(); } - } - else if (e.button == 1 || e.button == 2) - { + } else if (e.button == 1 || e.button == 2) { //check drag threshold for larger screens - if (e.delta.magnitude > dragThreshold) - { + if (e.delta.magnitude > dragThreshold) { panOffset += e.delta * zoom; isPanning = true; } @@ -183,23 +153,17 @@ namespace XNodeEditor break; case EventType.MouseDown: Repaint(); - if (e.button == 0) - { + if (e.button == 0) { draggedOutputReroutes.Clear(); - if (IsHoveringPort) - { - if (hoveredPort.IsOutput) - { + if (IsHoveringPort) { + if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; autoConnectOutput = hoveredPort; - } - else - { + } else { hoveredPort.VerifyConnections(); autoConnectOutput = null; - if (hoveredPort.IsConnected) - { + if (hoveredPort.IsConnected) { XNode.Node node = hoveredPort.node; XNode.NodePort output = hoveredPort.Connection; int outputConnectionIndex = output.GetConnectionIndex(hoveredPort); @@ -210,33 +174,25 @@ namespace XNodeEditor if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); } } - } - else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) - { + } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { // If mousedown on node header, select or deselect - if (!Selection.Contains(hoveredNode)) - { + 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); + } 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) - { + } else if (IsHoveringReroute) { // If reroute isn't selected - if (!selectedReroutes.Contains(hoveredReroute)) - { + if (!selectedReroutes.Contains(hoveredReroute)) { // Add it if (e.control || e.shift) selectedReroutes.Add(hoveredReroute); // Select it - else - { + else { selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } @@ -248,11 +204,9 @@ namespace XNodeEditor currentActivity = NodeActivity.HoldNode; } // If mousedown on grid background, deselect all - else if (!IsHoveringNode) - { + else if (!IsHoveringNode) { currentActivity = NodeActivity.HoldGrid; - if (!e.control && !e.shift) - { + if (!e.control && !e.shift) { selectedReroutes.Clear(); Selection.activeObject = null; } @@ -260,29 +214,24 @@ namespace XNodeEditor } break; case EventType.MouseUp: - if (e.button == 0) - { + if (e.button == 0) { //Port drag release - if (IsDraggingPort) - { + if (IsDraggingPort) { // If connection is valid, save it - if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) - { + if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) { 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) - { + if (connectionIndex != -1) { draggedOutput.GetReroutePoints(connectionIndex).AddRange(draggedOutputReroutes); if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node); EditorUtility.SetDirty(graph); } } // Open context menu for auto-connection if there is no target node - else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) - { + else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) { GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu, draggedOutput.ValueType); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -292,18 +241,13 @@ namespace XNodeEditor draggedOutputTarget = null; EditorUtility.SetDirty(graph); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); - } - else if (currentActivity == NodeActivity.DragNode) - { + } else if (currentActivity == NodeActivity.DragNode) { IEnumerable 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) - { + } else if (!IsHoveringNode) { // If click outside node, release field focus - if (!isPanning) - { + if (!isPanning) { EditorGUI.FocusTextInControl(null); EditorGUIUtility.editingTextField = false; } @@ -311,61 +255,44 @@ namespace XNodeEditor } // If click node header, select it. - if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) - { + if (currentActivity == NodeActivity.HoldNode && !(e.control || e.shift)) { selectedReroutes.Clear(); SelectNode(hoveredNode, false); // Double click to center node - if (isDoubleClick) - { + 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)) - { + if (IsHoveringReroute && !(e.control || e.shift)) { selectedReroutes = new List() { hoveredReroute }; Selection.activeObject = null; } Repaint(); currentActivity = NodeActivity.Idle; - } - else if (e.button == 1 || e.button == 2) - { - if (!isPanning) - { - if (IsDraggingPort) - { + } 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) - { + } 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) - { + } else if (IsHoveringReroute) { ShowRerouteContextMenu(hoveredReroute); - } - else if (IsHoveringPort) - { + } else if (IsHoveringPort) { ShowPortContextMenu(hoveredPort); - } - else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) - { + } 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) - { + } else if (!IsHoveringNode) { autoConnectOutput = null; GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); @@ -380,27 +307,18 @@ namespace XNodeEditor case EventType.KeyDown: if (EditorGUIUtility.editingTextField) break; else if (e.keyCode == KeyCode.F) Home(); - if (NodeEditorUtilities.IsMac()) - { + if (NodeEditorUtilities.IsMac()) { if (e.keyCode == KeyCode.Return) RenameSelectedNode(); - } - else - { + } 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) - { + 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) - { + } else { + foreach (XNode.Node node in graph.nodes) { SelectNode(node, true); } } @@ -409,28 +327,19 @@ namespace XNodeEditor break; case EventType.ValidateCommand: case EventType.ExecuteCommand: - if (e.commandName == "SoftDelete") - { + if (e.commandName == "SoftDelete") { if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes(); e.Use(); - } - else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") - { + } else if (NodeEditorUtilities.IsMac() && e.commandName == "Delete") { if (e.type == EventType.ExecuteCommand) RemoveSelectedNodes(); e.Use(); - } - else if (e.commandName == "Duplicate") - { + } else if (e.commandName == "Duplicate") { if (e.type == EventType.ExecuteCommand) DuplicateSelectedNodes(); e.Use(); - } - else if (e.commandName == "Copy") - { + } else if (e.commandName == "Copy") { if (e.type == EventType.ExecuteCommand) CopySelectedNodes(); e.Use(); - } - else if (e.commandName == "Paste") - { + } else if (e.commandName == "Paste") { if (e.type == EventType.ExecuteCommand) PasteNodes(WindowToGridPosition(lastMousePosition)); e.Use(); } @@ -438,8 +347,7 @@ namespace XNodeEditor break; case EventType.Ignore: // If release mouse outside window - if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) - { + if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { Repaint(); currentActivity = NodeActivity.Idle; } @@ -447,57 +355,45 @@ namespace XNodeEditor } } - private void RecalculateDragOffsets(Event current) - { + 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 XNode.Node) - { + 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; dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition); } } // Selected reroutes - for (int i = 0; i < selectedReroutes.Count; i++) - { + for (int i = 0; i < selectedReroutes.Count; i++) { dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition); } } /// Puts all selected nodes in focus. If no nodes are present, resets view and zoom to to origin - public void Home() - { + public void Home() { var nodes = Selection.objects.Where(o => o is XNode.Node).Cast().ToList(); - if (nodes.Count > 0) - { + if (nodes.Count > 0) { Vector2 minPos = nodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); Vector2 maxPos = nodes.Select(x => x.position + (nodeSizes.ContainsKey(x) ? nodeSizes[x] : Vector2.zero)).Aggregate((x, y) => new Vector2(Mathf.Max(x.x, y.x), Mathf.Max(x.y, y.y))); panOffset = -(minPos + (maxPos - minPos) / 2f); - } - else - { + } else { zoom = 2; panOffset = Vector2.zero; } } /// Remove nodes in the graph in Selection.objects - public void RemoveSelectedNodes() - { + 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++) - { + for (int i = 0; i < selectedReroutes.Count; i++) { selectedReroutes[i].RemovePoint(); } selectedReroutes.Clear(); - foreach (UnityEngine.Object item in Selection.objects) - { - if (item is XNode.Node) - { + foreach (UnityEngine.Object item in Selection.objects) { + if (item is XNode.Node) { XNode.Node node = item as XNode.Node; graphEditor.RemoveNode(node); } @@ -505,37 +401,29 @@ namespace XNodeEditor } /// Initiate a rename on the currently selected node - public void RenameSelectedNode() - { - if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) - { + public void RenameSelectedNode() { + if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; Vector2 size; - if (nodeSizes.TryGetValue(node, out size)) - { + if (nodeSizes.TryGetValue(node, out size)) { RenamePopup.Show(Selection.activeObject, size.x); - } - else - { + } else { RenamePopup.Show(Selection.activeObject); } } } /// Draw this node on top of other nodes by placing it last in the graph.nodes list - public void MoveNodeToTop(XNode.Node node) - { + public void MoveNodeToTop(XNode.Node node) { int index; - while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) - { + while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { graph.nodes[index] = graph.nodes[index + 1]; graph.nodes[index + 1] = node; } } /// Duplicate selected nodes and select the duplicates - public void DuplicateSelectedNodes() - { + public void DuplicateSelectedNodes() { // Get selected nodes which are part of this graph XNode.Node[] selectedNodes = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); if (selectedNodes == null || selectedNodes.Length == 0) return; @@ -544,18 +432,15 @@ namespace XNodeEditor InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30)); } - public void CopySelectedNodes() - { + public void CopySelectedNodes() { copyBuffer = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); } - public void PasteNodes(Vector2 pos) - { + public void PasteNodes(Vector2 pos) { InsertDuplicateNodes(copyBuffer, pos); } - private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) - { + private void InsertDuplicateNodes(XNode.Node[] nodes, Vector2 topLeft) { if (nodes == null || nodes.Length == 0) return; // Get top-left node @@ -564,16 +449,14 @@ namespace XNodeEditor UnityEngine.Object[] newNodes = new UnityEngine.Object[nodes.Length]; Dictionary substitutes = new Dictionary(); - for (int i = 0; i < nodes.Length; i++) - { + for (int i = 0; i < nodes.Length; i++) { XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; Type nodeType = srcNode.GetType(); - if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) - { + if (NodeEditorUtilities.GetAttrib(nodeType, out disallowAttrib)) { int typeCount = graph.nodes.Count(x => x.GetType() == nodeType); if (typeCount >= disallowAttrib.max) continue; } @@ -585,20 +468,16 @@ namespace XNodeEditor } // Walk through the selected nodes again, recreate connections, using the new nodes - for (int i = 0; i < nodes.Length; i++) - { + for (int i = 0; i < nodes.Length; i++) { XNode.Node srcNode = nodes[i]; if (srcNode == null) continue; - foreach (XNode.NodePort port in srcNode.Ports) - { - for (int c = 0; c < port.ConnectionCount; c++) - { + foreach (XNode.NodePort port in srcNode.Ports) { + for (int c = 0; c < port.ConnectionCount; c++) { XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c); XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c); XNode.Node newNodeIn, newNodeOut; - if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) - { + if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) { newNodeIn.UpdatePorts(); newNodeOut.UpdatePorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); @@ -614,10 +493,8 @@ namespace XNodeEditor } /// Draw a connection as we are dragging it - public void DrawDraggedConnection() - { - if (IsDraggingPort) - { + public void DrawDraggedConnection() { + if (IsDraggingPort) { Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); float thickness = graphEditor.GetNoodleThickness(draggedOutput, null); NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null); @@ -627,8 +504,7 @@ namespace XNodeEditor if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return; List gridPoints = new List(); gridPoints.Add(fromRect.center); - for (int i = 0; i < draggedOutputReroutes.Count; i++) - { + for (int i = 0; i < draggedOutputReroutes.Count; i++) { gridPoints.Add(draggedOutputReroutes[i]); } if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center); @@ -643,8 +519,7 @@ namespace XNodeEditor frcol.a = 0.6f; // Loop through reroute points again and draw the points - for (int i = 0; i < draggedOutputReroutes.Count; i++) - { + for (int i = 0; i < draggedOutputReroutes.Count; i++) { // Draw reroute point at position Rect rect = new Rect(draggedOutputReroutes[i], new Vector2(16, 16)); rect.position = new Vector2(rect.position.x - 8, rect.position.y - 8); @@ -655,8 +530,7 @@ namespace XNodeEditor } } - bool IsHoveringTitle(XNode.Node node) - { + bool IsHoveringTitle(XNode.Node node) { Vector2 mousePos = Event.current.mousePosition; //Get node position Vector2 nodePos = GridToWindowPosition(node.position); @@ -669,8 +543,7 @@ namespace XNodeEditor } /// Attempt to connect dragged output to target node - public void AutoConnect(XNode.Node node) - { + public void AutoConnect(XNode.Node node) { if (autoConnectOutput == null) return; // Find input port of same type diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 78c9614..ca726d5 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -119,8 +119,7 @@ namespace XNodeEditor { } contextMenu.AddItem(new GUIContent("Clear Connections"), false, () => hoveredPort.ClearConnections()); //Get compatible nodes with this port - if (NodeEditorPreferences.GetSettings().createFilter) - { + if (NodeEditorPreferences.GetSettings().createFilter) { contextMenu.AddSeparator(""); if (hoveredPort.direction == XNode.NodePort.IO.Input) @@ -573,8 +572,7 @@ namespace XNodeEditor { string tooltip = null; if (hoveredPort != null) { tooltip = graphEditor.GetPortTooltip(hoveredPort); - } - else if (hoveredNode != null && IsHoveringNode && IsHoveringTitle(hoveredNode)) { + } else if (hoveredNode != null && IsHoveringNode && IsHoveringTitle(hoveredNode)) { tooltip = NodeEditor.GetEditor(hoveredNode, this).GetHeaderTooltip(); } if (string.IsNullOrEmpty(tooltip)) return; diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 9a6cc1b..8c93cb2 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -7,24 +7,20 @@ using UnityEditor; using UnityEditorInternal; using UnityEngine; -namespace XNodeEditor -{ +namespace XNodeEditor { /// xNode-specific version of - public static class NodeEditorGUILayout - { + public static class NodeEditorGUILayout { private static readonly Dictionary> reorderableListCache = new Dictionary>(); private static int reorderableListIndex = -1; /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) - { + public static void PropertyField(SerializedProperty property, bool includeChildren = true, params GUILayoutOption[] options) { PropertyField(property, (GUIContent)null, includeChildren, options); } /// Make a field for a serialized property. Automatically displays relevant node port. - public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) - { + public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) throw new NullReferenceException(); XNode.Node node = property.serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(property.name); @@ -32,33 +28,28 @@ namespace XNodeEditor } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) - { + public static void PropertyField(SerializedProperty property, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) { PropertyField(property, null, port, includeChildren, options); } /// Make a field for a serialized property. Manual node port override. - public static void PropertyField(SerializedProperty property, GUIContent label, XNode.NodePort port, bool includeChildren = true, params GUILayoutOption[] options) - { + 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 - { + else { Rect rect = new Rect(); List 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) - { + 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)) - { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out inputAttribute)) { dynamicPortList = inputAttribute.dynamicPortList; showBacking = inputAttribute.backingValue; } @@ -69,40 +60,30 @@ namespace XNodeEditor float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) - { - if (attr is SpaceAttribute) - { + 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) - { + } 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; - } - else if (attr is TooltipAttribute) - { + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } else if (attr is TooltipAttribute) { tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) - { + 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) - { + 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, tooltip)); @@ -123,15 +104,12 @@ namespace XNodeEditor float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.left; rect.position = rect.position - new Vector2(16 + paddingLeft, -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) - { + } 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)) - { + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out outputAttribute)) { dynamicPortList = outputAttribute.dynamicPortList; showBacking = outputAttribute.backingValue; } @@ -142,40 +120,30 @@ namespace XNodeEditor float spacePadding = 0; string tooltip = null; - foreach (var attr in propertyAttributes) - { - if (attr is SpaceAttribute) - { + 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) - { + } 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; - } - else if (attr is TooltipAttribute) - { + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } else if (attr is TooltipAttribute) { tooltip = (attr as TooltipAttribute).tooltip; } } - if (dynamicPortList) - { + 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) - { + 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, tooltip), NodeEditorResources.OutputPort, GUILayout.MinWidth(30)); @@ -210,30 +178,26 @@ namespace XNodeEditor } } - private static System.Type GetType(SerializedProperty property) - { + private static System.Type GetType(SerializedProperty property) { System.Type parentType = property.serializedObject.targetObject.GetType(); System.Reflection.FieldInfo fi = parentType.GetFieldInfo(property.name); return fi.FieldType; } /// Make a simple port field. - public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) - { + public static void PortField(XNode.NodePort port, params GUILayoutOption[] options) { PortField(null, port, options); } /// Make a simple port field. - public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) - { + public static void PortField(GUIContent label, XNode.NodePort port, params GUILayoutOption[] options) { if (port == null) return; if (options == null) options = new GUILayoutOption[] { GUILayout.MinWidth(30) }; Vector2 position = Vector3.zero; GUIContent content = label != null ? label : new GUIContent(ObjectNames.NicifyVariableName(port.fieldName)); // 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) - { + if (port.direction == XNode.NodePort.IO.Input) { // Display a label EditorGUILayout.LabelField(content, options); @@ -242,8 +206,7 @@ namespace XNodeEditor position = rect.position - new Vector2(16 + paddingLeft, 0); } // 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) - { + else if (port.direction == XNode.NodePort.IO.Output) { // Display a label EditorGUILayout.LabelField(content, NodeEditorResources.OutputPort, options); @@ -255,8 +218,7 @@ namespace XNodeEditor } /// Make a simple port field. - public static void PortField(Vector2 position, XNode.NodePort port) - { + public static void PortField(Vector2 position, XNode.NodePort port) { if (port == null) return; Rect rect = new Rect(position, new Vector2(16, 16)); @@ -273,21 +235,17 @@ namespace XNodeEditor } /// Add a port field to previous layout element. - public static void AddPortField(XNode.NodePort port) - { + public static void AddPortField(XNode.NodePort port) { if (port == null) return; 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 == XNode.NodePort.IO.Input) - { + if (port.direction == XNode.NodePort.IO.Input) { rect = GUILayoutUtility.GetLastRect(); float paddingLeft = NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.left; rect.position = rect.position - new Vector2(16 + paddingLeft, 0); // 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) - { + } else if (port.direction == XNode.NodePort.IO.Output) { rect = GUILayoutUtility.GetLastRect(); rect.width += NodeEditorWindow.current.graphEditor.GetPortStyle(port).padding.right; rect.position = rect.position + new Vector2(rect.width, 0); @@ -307,8 +265,7 @@ namespace XNodeEditor } /// Draws an input and an output port on the same line - public static void PortPair(XNode.NodePort input, XNode.NodePort output) - { + public static void PortPair(XNode.NodePort input, XNode.NodePort output) { GUILayout.BeginHorizontal(); NodeEditorGUILayout.PortField(input, GUILayout.MinWidth(0)); NodeEditorGUILayout.PortField(output, GUILayout.MinWidth(0)); @@ -323,8 +280,7 @@ namespace XNodeEditor /// /// texture for border of the dot port /// texture for the dot port - public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor, Texture2D border, Texture2D dot) - { + public static void DrawPortHandle(Rect rect, Color backgroundColor, Color typeColor, Texture2D border, Texture2D dot) { Color col = GUI.color; GUI.color = backgroundColor; GUI.DrawTexture(rect, border); @@ -336,26 +292,22 @@ namespace XNodeEditor #region Obsolete [Obsolete("Use IsDynamicPortListPort instead")] - public static bool IsInstancePortListPort(XNode.NodePort port) - { + public static bool IsInstancePortListPort(XNode.NodePort port) { return IsDynamicPortListPort(port); } [Obsolete("Use DynamicPortList instead")] - public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) - { + public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation); } #endregion /// Is this port part of a DynamicPortList? - public static bool IsDynamicPortListPort(XNode.NodePort port) - { + public static bool IsDynamicPortListPort(XNode.NodePort port) { string[] parts = port.fieldName.Split(' '); if (parts.Length != 2) return false; Dictionary cache; - if (reorderableListCache.TryGetValue(port.node, out cache)) - { + if (reorderableListCache.TryGetValue(port.node, out cache)) { ReorderableList list; if (cache.TryGetValue(parts[0], out list)) return true; } @@ -368,18 +320,14 @@ namespace XNodeEditor /// The serializedObject of the node /// Connection type of added dynamic ports /// Called on the list on creation. Use this if you want to customize the created ReorderableList - public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) - { + public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { XNode.Node node = serializedObject.targetObject as XNode.Node; - var indexedPorts = node.DynamicPorts.Select(x => - { + var indexedPorts = node.DynamicPorts.Select(x => { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) - { + if (split != null && split.Length == 2 && split[0] == fieldName) { int i = -1; - if (int.TryParse(split[1], out i)) - { + if (int.TryParse(split[1], out i)) { return new { index = i, port = x }; } } @@ -391,13 +339,11 @@ namespace XNodeEditor ReorderableList list = null; Dictionary rlc; - if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) - { + if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) { if (!rlc.TryGetValue(fieldName, out list)) list = null; } // If a ReorderableList isn't cached for this array, do so. - if (list == null) - { + if (list == null) { SerializedProperty arrayData = serializedObject.FindProperty(fieldName); list = CreateReorderableList(fieldName, dynamicPorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, onCreation); if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) rlc.Add(fieldName, list); @@ -408,67 +354,53 @@ namespace XNodeEditor } - private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) - { + private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) { bool hasArrayData = arrayData != null && arrayData.isArray; XNode.Node node = serializedObject.targetObject as XNode.Node; ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true); string label = arrayData != null ? arrayData.displayName : ObjectNames.NicifyVariableName(fieldName); list.drawElementCallback = - (Rect rect, int index, bool isActive, bool isFocused) => - { + (Rect rect, int index, bool isActive, bool isFocused) => { XNode.NodePort port = node.GetPort(fieldName + " " + index); - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) - { - if (arrayData.arraySize <= index) - { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { + if (arrayData.arraySize <= index) { EditorGUI.LabelField(rect, "Array[" + index + "] data out of range"); return; } SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); EditorGUI.PropertyField(rect, itemData, true); - } - else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); - if (port != null) - { + } else EditorGUI.LabelField(rect, port != null ? port.fieldName : ""); + if (port != null) { Vector2 pos = rect.position + (port.IsOutput ? new Vector2(rect.width + 6, 0) : new Vector2(-36, 0)); NodeEditorGUILayout.PortField(pos, port); } }; list.elementHeightCallback = - (int index) => - { - if (hasArrayData) - { + (int index) => { + if (hasArrayData) { if (arrayData.arraySize <= index) return EditorGUIUtility.singleLineHeight; SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); return EditorGUI.GetPropertyHeight(itemData); - } - else return EditorGUIUtility.singleLineHeight; + } else return EditorGUIUtility.singleLineHeight; }; list.drawHeaderCallback = - (Rect rect) => - { + (Rect rect) => { EditorGUI.LabelField(rect, label); }; list.onSelectCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { reorderableListIndex = rl.index; }; list.onReorderCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { bool hasRect = false; bool hasNewRect = false; Rect rect = Rect.zero; Rect newRect = Rect.zero; // Move up - if (rl.index > reorderableListIndex) - { - for (int i = reorderableListIndex; i < rl.index; ++i) - { + if (rl.index > reorderableListIndex) { + for (int i = reorderableListIndex; i < rl.index; ++i) { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i + 1)); port.SwapConnections(nextPort); @@ -481,10 +413,8 @@ namespace XNodeEditor } } // Move down - else - { - for (int i = reorderableListIndex; i > rl.index; --i) - { + else { + for (int i = reorderableListIndex; i > rl.index; --i) { XNode.NodePort port = node.GetPort(fieldName + " " + i); XNode.NodePort nextPort = node.GetPort(fieldName + " " + (i - 1)); port.SwapConnections(nextPort); @@ -501,8 +431,7 @@ namespace XNodeEditor serializedObject.Update(); // Move array data if there is any - if (hasArrayData) - { + if (hasArrayData) { arrayData.MoveArrayElement(reorderableListIndex, rl.index); } @@ -513,8 +442,7 @@ namespace XNodeEditor EditorApplication.delayCall += NodeEditorWindow.current.Repaint; }; list.onAddCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { // Add dynamic port postfixed with an index number string newName = fieldName + " 0"; int i = 0; @@ -524,24 +452,19 @@ namespace XNodeEditor else node.AddDynamicInput(type, connectionType, typeConstraint, newName); serializedObject.Update(); EditorUtility.SetDirty(node); - if (hasArrayData) - { + if (hasArrayData) { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); }; list.onRemoveCallback = - (ReorderableList rl) => - { + (ReorderableList rl) => { - var indexedPorts = node.DynamicPorts.Select(x => - { + var indexedPorts = node.DynamicPorts.Select(x => { string[] split = x.fieldName.Split(' '); - if (split != null && split.Length == 2 && split[0] == fieldName) - { + if (split != null && split.Length == 2 && split[0] == fieldName) { int i = -1; - if (int.TryParse(split[1], out i)) - { + if (int.TryParse(split[1], out i)) { return new { index = i, port = x }; } } @@ -551,24 +474,17 @@ namespace XNodeEditor int index = rl.index; - if (dynamicPorts[index] == null) - { + if (dynamicPorts[index] == null) { Debug.LogWarning("No port found at index " + index + " - Skipped"); - } - else if (dynamicPorts.Count <= index) - { + } else if (dynamicPorts.Count <= index) { Debug.LogWarning("DynamicPorts[" + index + "] out of range. Length was " + dynamicPorts.Count + " - Skipped"); - } - else - { + } else { // Clear the removed ports connections dynamicPorts[index].ClearConnections(); // Move following connections one step up to replace the missing connection - for (int k = index + 1; k < dynamicPorts.Count(); k++) - { - for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) - { + for (int k = index + 1; k < dynamicPorts.Count(); k++) { + for (int j = 0; j < dynamicPorts[k].ConnectionCount; j++) { XNode.NodePort other = dynamicPorts[k].GetConnection(j); dynamicPorts[k].Disconnect(other); dynamicPorts[k - 1].Connect(other); @@ -580,20 +496,16 @@ namespace XNodeEditor EditorUtility.SetDirty(node); } - if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) - { - if (arrayData.arraySize <= index) - { + if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) { + if (arrayData.arraySize <= index) { Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped"); Debug.Log(rl.list[0]); return; } arrayData.DeleteArrayElementAtIndex(index); // Error handling. If the following happens too often, file a bug report at https://github.com/Siccity/xNode/issues - if (dynamicPorts.Count <= arrayData.arraySize) - { - while (dynamicPorts.Count <= arrayData.arraySize) - { + if (dynamicPorts.Count <= arrayData.arraySize) { + while (dynamicPorts.Count <= arrayData.arraySize) { arrayData.DeleteArrayElementAtIndex(arrayData.arraySize - 1); } UnityEngine.Debug.LogWarning("Array size exceeded dynamic ports size. Excess items removed."); @@ -603,11 +515,9 @@ namespace XNodeEditor } }; - if (hasArrayData) - { + if (hasArrayData) { int dynamicPortCount = dynamicPorts.Count; - while (dynamicPortCount < arrayData.arraySize) - { + while (dynamicPortCount < arrayData.arraySize) { // Add dynamic port postfixed with an index number string newName = arrayData.name + " 0"; int i = 0; @@ -617,8 +527,7 @@ namespace XNodeEditor EditorUtility.SetDirty(node); dynamicPortCount++; } - while (arrayData.arraySize < dynamicPortCount) - { + while (arrayData.arraySize < dynamicPortCount) { arrayData.InsertArrayElementAtIndex(arrayData.arraySize); } serializedObject.ApplyModifiedProperties(); diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 715ac6a..5113904 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -104,7 +104,7 @@ namespace XNodeEditor { public static SettingsProvider CreateXNodeSettingsProvider() { SettingsProvider provider = new SettingsProvider("Preferences/Node Editor", SettingsScope.User) { guiHandler = (searchContext) => { XNodeEditor.NodeEditorPreferences.PreferencesGUI(); }, - keywords = new HashSet(new [] { "xNode", "node", "editor", "graph", "connections", "noodles", "ports" }) + keywords = new HashSet(new [] { "xNode", "node", "editor", "graph", "connections", "noodles", "ports" }) }; return provider; } @@ -166,9 +166,8 @@ namespace XNodeEditor { settings.noodleThickness = EditorGUILayout.FloatField(new GUIContent("Noodle thickness", "Noodle Thickness of the node connections"), settings.noodleThickness); settings.noodleStroke = (NoodleStroke) EditorGUILayout.EnumPopup("Noodle stroke", (Enum) settings.noodleStroke); settings.portTooltips = EditorGUILayout.Toggle("Port Tooltips", settings.portTooltips); - settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); + settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); settings.createFilter = EditorGUILayout.Toggle(new GUIContent("Create Filter", "Only show nodes that are compatible with the selected port"), settings.createFilter); - //END if (GUI.changed) { diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index 900e2e2..753973b 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -134,21 +134,17 @@ namespace XNodeEditor { /// Type to find compatiblities /// /// True if NodeType has some port with value type compatible - public static bool HasCompatiblePortType(Type nodeType, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) - { + public static bool HasCompatiblePortType(Type nodeType, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Type findType = typeof(XNode.Node.InputAttribute); if (direction == XNode.NodePort.IO.Output) findType = typeof(XNode.Node.OutputAttribute); //Get All fields from node type and we go filter only field with portAttribute. //This way is possible to know the values of the all ports and if have some with compatible value tue - foreach (FieldInfo f in XNode.NodeDataCache.GetNodeFields(nodeType)) - { + foreach (FieldInfo f in XNode.NodeDataCache.GetNodeFields(nodeType)) { var portAttribute = f.GetCustomAttributes(findType, false).FirstOrDefault(); - if (portAttribute != null) - { - if (IsCastableTo(f.FieldType, compatibleType)) - { + if (portAttribute != null) { + if (IsCastableTo(f.FieldType, compatibleType)) { return true; } } @@ -163,8 +159,7 @@ namespace XNodeEditor { /// List with all nodes type to filter /// Compatible Type to Filter /// Return Only Node Types with ports compatible, or an empty list - public static List GetCompatibleNodesTypes(Type[] nodeTypes, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) - { + public static List GetCompatibleNodesTypes(Type[] nodeTypes, Type compatibleType, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { //Result List List filteredTypes = new List(); @@ -173,10 +168,8 @@ namespace XNodeEditor { if (compatibleType == null) { return filteredTypes; } //Find compatiblity - foreach (Type findType in nodeTypes) - { - if (HasCompatiblePortType(findType, compatibleType, direction)) - { + foreach (Type findType in nodeTypes) { + if (HasCompatiblePortType(findType, compatibleType, direction)) { filteredTypes.Add(findType); } } diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 580e3a8..9b80df0 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -4,12 +4,10 @@ using System.Linq; using UnityEditor; using UnityEngine; -namespace XNodeEditor -{ +namespace XNodeEditor { /// Base class to derive custom Node Graph editors from. Use this to override how graphs are drawn in the editor. [CustomNodeGraphEditor(typeof(XNode.NodeGraph))] - public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase - { + public class NodeGraphEditor : XNodeEditor.Internal.NodeEditorBase { [Obsolete("Use window.position instead")] public Rect position { get { return window.position; } set { window.position = value; } } /// Are we currently renaming a node? @@ -26,25 +24,21 @@ namespace XNodeEditor /// Called when NodeEditorWindow loses focus public virtual void OnWindowFocusLost() { } - public virtual Texture2D GetGridTexture() - { + public virtual Texture2D GetGridTexture() { return NodeEditorPreferences.GetSettings().gridTexture; } - public virtual Texture2D GetSecondaryGridTexture() - { + public virtual Texture2D GetSecondaryGridTexture() { return NodeEditorPreferences.GetSettings().crossTexture; } /// Return default settings for this graph type. This is the settings the user will load if no previous settings have been saved. - public virtual NodeEditorPreferences.Settings GetDefaultPreferences() - { + public virtual NodeEditorPreferences.Settings GetDefaultPreferences() { return new NodeEditorPreferences.Settings(); } /// Returns context node menu path. Null or empty strings for hidden nodes. - public virtual string GetNodeMenuName(Type type) - { + public virtual string GetNodeMenuName(Type type) { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -54,8 +48,7 @@ namespace XNodeEditor } /// The order by which the menu items are displayed. - public virtual int GetNodeMenuOrder(Type type) - { + public virtual int GetNodeMenuOrder(Type type) { //Check if type has the CreateNodeMenuAttribute XNode.Node.CreateNodeMenuAttribute attrib; if (NodeEditorUtilities.GetAttrib(type, out attrib)) // Return custom path @@ -65,25 +58,22 @@ namespace XNodeEditor } /// - /// Add items for the context menu when right-clicking this node. + /// Add items for the context menu when right-clicking this node. /// Override to add custom menu items. /// /// /// Use it to filter only nodes with ports value type, compatible with this type /// Direction of the compatiblity - public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) - { + public virtual void AddContextMenuItems(GenericMenu menu, Type compatibleType = null, XNode.NodePort.IO direction = XNode.NodePort.IO.Input) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); Type[] nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); - if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) - { + if (compatibleType != null && NodeEditorPreferences.GetSettings().createFilter) { nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); } - for (int i = 0; i < nodeTypes.Length; i++) - { + for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; @@ -94,16 +84,14 @@ namespace XNodeEditor // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; bool disallowed = false; - if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) - { + if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) { int typeCount = target.nodes.Count(x => x.GetType() == type); if (typeCount >= disallowAttrib.max) disallowed = true; } // Add node entry to context menu if (disallowed) menu.AddItem(new GUIContent(path), false, null); - else menu.AddItem(new GUIContent(path), false, () => - { + else menu.AddItem(new GUIContent(path), false, () => { XNode.Node node = CreateNode(type, pos); NodeEditorWindow.current.AutoConnect(node); }); @@ -115,17 +103,14 @@ namespace XNodeEditor menu.AddCustomContextMenuItems(target); } - /// Returned gradient is used to color noodles /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) - { + public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) { Gradient grad = new Gradient(); // If dragging the noodle, draw solid, slightly transparent - if (input == null) - { + if (input == null) { Color a = GetTypeColor(output.ValueType); grad.SetKeys( new GradientColorKey[] { new GradientColorKey(a, 0f) }, @@ -133,13 +118,11 @@ namespace XNodeEditor ); } // If normal, draw gradient fading from one input color to the other - else - { + else { Color a = GetTypeColor(output.ValueType); Color b = GetTypeColor(input.ValueType); // If any port is hovered, tint white - if (window.hoveredPort == output || window.hoveredPort == input) - { + if (window.hoveredPort == output || window.hoveredPort == input) { a = Color.Lerp(a, Color.white, 0.8f); b = Color.Lerp(b, Color.white, 0.8f); } @@ -154,24 +137,20 @@ namespace XNodeEditor /// Returned float is used for noodle thickness /// The output this noodle comes from. Never null. /// The output this noodle comes from. Can be null if we are dragging the noodle. - public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) - { + public virtual float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) { return NodeEditorPreferences.GetSettings().noodleThickness; } - public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) - { + public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) { return NodeEditorPreferences.GetSettings().noodlePath; } - public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) - { + public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) { return NodeEditorPreferences.GetSettings().noodleStroke; } /// Returned color is used to color ports - public virtual Color GetPortColor(XNode.NodePort port) - { + public virtual Color GetPortColor(XNode.NodePort port) { return GetTypeColor(port.ValueType); } @@ -185,8 +164,7 @@ namespace XNodeEditor /// /// the owner of the style /// - public virtual GUIStyle GetPortStyle(XNode.NodePort port) - { + public virtual GUIStyle GetPortStyle(XNode.NodePort port) { if (port.direction == XNode.NodePort.IO.Input) return NodeEditorResources.styles.inputPort; @@ -195,25 +173,21 @@ namespace XNodeEditor /// The returned color is used to color the background of the door. /// Usually used for outer edge effect - public virtual Color GetPortBackgroundColor(XNode.NodePort port) - { + public virtual Color GetPortBackgroundColor(XNode.NodePort port) { return Color.gray; } /// Returns generated color for a type. This color is editable in preferences - public virtual Color GetTypeColor(Type type) - { + public virtual Color GetTypeColor(Type type) { return NodeEditorPreferences.GetTypeColor(type); } /// Override to display custom tooltips - public virtual string GetPortTooltip(XNode.NodePort port) - { + public virtual string GetPortTooltip(XNode.NodePort port) { Type portType = port.ValueType; string tooltip = ""; tooltip = portType.PrettyName(); - if (port.IsOutput) - { + if (port.IsOutput) { object obj = port.node.GetValue(port); tooltip += " = " + (obj != null ? obj.ToString() : "null"); } @@ -221,14 +195,12 @@ namespace XNodeEditor } /// Deal with objects dropped into the graph through DragAndDrop - public virtual void OnDropObjects(UnityEngine.Object[] objects) - { + public virtual void OnDropObjects(UnityEngine.Object[] objects) { if (GetType() != typeof(NodeGraphEditor)) Debug.Log("No OnDropObjects override defined for " + GetType()); } /// Create a node and save it in the graph asset - public virtual XNode.Node CreateNode(Type type, Vector2 position) - { + public virtual XNode.Node CreateNode(Type type, Vector2 position) { Undo.RecordObject(target, "Create Node"); XNode.Node node = target.AddNode(type); Undo.RegisterCreatedObjectUndo(node, "Create Node"); @@ -241,8 +213,7 @@ namespace XNodeEditor } /// Creates a copy of the original node in the graph - public virtual XNode.Node CopyNode(XNode.Node original) - { + public virtual XNode.Node CopyNode(XNode.Node original) { Undo.RecordObject(target, "Duplicate Node"); XNode.Node node = target.CopyNode(original); Undo.RegisterCreatedObjectUndo(node, "Duplicate Node"); @@ -253,16 +224,13 @@ namespace XNodeEditor } /// Return false for nodes that can't be removed - public virtual bool CanRemove(XNode.Node node) - { + public virtual bool CanRemove(XNode.Node node) { // Check graph attributes to see if this node is required Type graphType = target.GetType(); XNode.NodeGraph.RequireNodeAttribute[] attribs = Array.ConvertAll( graphType.GetCustomAttributes(typeof(XNode.NodeGraph.RequireNodeAttribute), true), x => x as XNode.NodeGraph.RequireNodeAttribute); - if (attribs.Any(x => x.Requires(node.GetType()))) - { - if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) - { + if (attribs.Any(x => x.Requires(node.GetType()))) { + if (target.nodes.Count(x => x.GetType() == node.GetType()) <= 1) { return false; } } @@ -270,8 +238,7 @@ namespace XNodeEditor } /// Safely remove a node and all its connections. - public virtual void RemoveNode(XNode.Node node) - { + public virtual void RemoveNode(XNode.Node node) { if (!CanRemove(node)) return; // Remove the node @@ -287,21 +254,18 @@ namespace XNodeEditor [AttributeUsage(AttributeTargets.Class)] public class CustomNodeGraphEditorAttribute : Attribute, - XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib - { + XNodeEditor.Internal.NodeEditorBase.INodeEditorAttrib { private Type inspectedType; public string editorPrefsKey; /// Tells a NodeGraphEditor which Graph type it is an editor for /// Type that this editor can edit /// Define unique key for unique layout settings instance - public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") - { + public CustomNodeGraphEditorAttribute(Type inspectedType, string editorPrefsKey = "xNode.Settings") { this.inspectedType = inspectedType; this.editorPrefsKey = editorPrefsKey; } - public Type GetInspectedType() - { + public Type GetInspectedType() { return inspectedType; } }