diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index e78aaec..a2b95b7 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -233,7 +233,7 @@ namespace XNodeEditor { // Open context menu for auto-connection if there is no target node 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 @@ -512,6 +512,7 @@ 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; @@ -524,7 +525,7 @@ namespace XNodeEditor { 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); } } } diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index d914eff..ca726d5 100755 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -118,6 +118,15 @@ 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().createFilter) { + 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(); } @@ -334,6 +343,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 +378,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; @@ -561,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 1b74a32..8c93cb2 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -16,7 +16,7 @@ namespace XNodeEditor { /// 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); + PropertyField(property, (GUIContent)null, includeChildren, options); } /// Make a field for a serialized property. Automatically displays relevant node port. @@ -101,7 +101,7 @@ 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) { @@ -161,7 +161,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 +169,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; @@ -201,7 +202,7 @@ namespace XNodeEditor { 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 @@ -210,7 +211,7 @@ namespace XNodeEditor { 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); @@ -224,7 +225,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; @@ -239,12 +242,12 @@ namespace XNodeEditor { // 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) { 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) { 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 +255,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; @@ -267,16 +272,25 @@ namespace XNodeEditor { 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) { return IsDynamicPortListPort(port); @@ -286,7 +300,7 @@ namespace XNodeEditor { 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) { @@ -317,7 +331,7 @@ namespace XNodeEditor { 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(); @@ -454,7 +468,7 @@ namespace XNodeEditor { 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(); diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index ace03b9..73dfe96 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -38,6 +38,7 @@ namespace XNodeEditor { public bool autoSave = true; public bool openOnCreate = true; public bool dragToCreate = true; + public bool createFilter = true; public bool zoomToMouse = true; public bool portTooltips = true; [SerializeField] private string typeColorsData = ""; @@ -104,7 +105,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; } @@ -168,6 +169,9 @@ 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); + settings.createFilter = EditorGUILayout.Toggle(new GUIContent("Create Filter", "Only show nodes that are compatible with the selected port"), settings.createFilter); + + //END if (GUI.changed) { SavePrefs(key, settings); NodeEditorWindow.RepaintAll(); 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/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index ac12e33..753973b 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -127,6 +127,57 @@ 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"; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index dcc92a9..9b80df0 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -57,11 +57,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 && NodeEditorPreferences.GetSettings().createFilter) { + nodeTypes = NodeEditorUtilities.GetCompatibleNodesTypes(NodeEditorReflection.nodeTypes, compatibleType, direction).ToArray(); + } + for (int i = 0; i < nodeTypes.Length; i++) { + Type type = nodeTypes[i]; //Get node context menu path @@ -141,6 +154,23 @@ namespace XNodeEditor { 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) {