From 0e7e4aa2545621b02f249357a6db68a22f74e61a Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Wed, 21 Sep 2022 13:53:28 +0200 Subject: [PATCH] Added public overrides for custom NodePort CanConnect conditions in GraphEditor --- Scripts/Editor/NodeEditorAction.cs | 11 ++++------- Scripts/Editor/NodeGraphEditor.cs | 7 +++++++ Scripts/NodeDataCache.cs | 19 +++++++++++-------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 5d8d32b..a9147f2 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -221,7 +221,7 @@ namespace XNodeEditor { //Port drag release if (IsDraggingPort) { // If connection is valid, save it - if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) { + if (draggedOutputTarget != null && graphEditor.CanConnect(draggedOutput, draggedOutputTarget)) { XNode.Node node = draggedOutputTarget.node; if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget); @@ -553,12 +553,9 @@ namespace XNodeEditor { public void AutoConnect(XNode.Node node) { if (autoConnectOutput == null) return; - // Find input port of same type - XNode.NodePort inputPort = node.Ports.FirstOrDefault(x => x.IsInput && x.ValueType == autoConnectOutput.ValueType); - // Fallback to input port - if (inputPort == null) inputPort = node.Ports.FirstOrDefault(x => x.IsInput); - // Autoconnect if connection is compatible - if (inputPort != null && inputPort.CanConnectTo(autoConnectOutput)) autoConnectOutput.Connect(inputPort); + // Find compatible input port + XNode.NodePort inputPort = node.Ports.FirstOrDefault(x => x.IsInput && graphEditor.CanConnect(autoConnectOutput, x)); + if (inputPort != null) autoConnectOutput.Connect(inputPort); // Save changes EditorUtility.SetDirty(graph); diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 0f5b8db..191d10b 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -59,6 +59,13 @@ namespace XNodeEditor { return 0; } + /// + /// Called before connecting two ports in the graph view to see if the output port is compatible with the input port + /// + public virtual bool CanConnect(XNode.NodePort output, XNode.NodePort input) { + return output.CanConnectTo(input); + } + /// /// Add items for the context menu when right-clicking this node. /// Override to add custom menu items. diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index f865ab2..3bc8478 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -70,20 +70,23 @@ namespace XNode { for (int i = 0; i < reconnectConnections.Count; i++) { NodePort connection = reconnectConnections[i]; if (connection == null) continue; + // CAVEAT: Ports connected under special conditions defined in graphEditor.CanConnect overrides will not auto-connect. + // To fix this, this code would need to be moved to an editor script and call graphEditor.CanConnect instead of port.CanConnectTo. + // This is only a problem in the rare edge case where user is using non-standard CanConnect overrides and changes port type of an already connected port if (port.CanConnectTo(connection)) port.Connect(connection); } } ports.Add(staticPort.fieldName, port); } } - + // Finally, make sure dynamic list port settings correspond to the settings of their "backing port" foreach (NodePort listPort in dynamicListPorts) { // At this point we know that ports here are dynamic list ports // which have passed name/"backing port" checks, ergo we can proceed more safely. string backingPortName = listPort.fieldName.Split(' ')[0]; NodePort backingPort = staticPorts[backingPortName]; - + // Update port constraints. Creating a new port instead will break the editor, mandating the need for setters. listPort.ValueType = GetBackingValueType(backingPort.ValueType); listPort.direction = backingPort.direction; @@ -95,7 +98,7 @@ namespace XNode { /// /// Extracts the underlying types from arrays and lists, the only collections for dynamic port lists /// currently supported. If the given type is not applicable (i.e. if the dynamic list port was not - /// defined as an array or a list), returns the given type itself. + /// defined as an array or a list), returns the given type itself. /// private static System.Type GetBackingValueType(System.Type portValType) { if (portValType.HasElementType) { @@ -114,10 +117,10 @@ namespace XNode { // Thus, we need to check for attributes... (but at least we don't need to look at all fields this time) string[] fieldNameParts = port.fieldName.Split(' '); if (fieldNameParts.Length != 2) return false; - + FieldInfo backingPortInfo = port.node.GetType().GetField(fieldNameParts[0]); if (backingPortInfo == null) return false; - + object[] attribs = backingPortInfo.GetCustomAttributes(true); return attribs.Any(x => { Node.InputAttribute inputAttribute = x as Node.InputAttribute; @@ -126,7 +129,7 @@ namespace XNode { outputAttribute != null && outputAttribute.dynamicPortList; }); } - + /// Cache node types private static void BuildCache() { portDataCache = new PortDataCache(); @@ -196,10 +199,10 @@ namespace XNode { portDataCache[nodeType].Add(new NodePort(fieldInfo[i])); } - if(formerlySerializedAsAttribute != null) { + if (formerlySerializedAsAttribute != null) { if (formerlySerializedAsCache == null) formerlySerializedAsCache = new Dictionary>(); if (!formerlySerializedAsCache.ContainsKey(nodeType)) formerlySerializedAsCache.Add(nodeType, new Dictionary()); - + if (formerlySerializedAsCache[nodeType].ContainsKey(formerlySerializedAsAttribute.oldName)) Debug.LogError("Another FormerlySerializedAs with value '" + formerlySerializedAsAttribute.oldName + "' already exist on this node."); else formerlySerializedAsCache[nodeType].Add(formerlySerializedAsAttribute.oldName, fieldInfo[i].Name); }