mirror of
https://github.com/Siccity/xNode.git
synced 2026-03-26 22:49:02 +08:00
Post fix 223: Allow dynamic port lists (declared as both arrays and lists) to connect to fields of their underlying types.
Such port lists are already drawn with elements of their underlying types, and allowing connections is logical.
This commit is contained in:
parent
5005b5e4f9
commit
3024b9e8d0
@ -5,14 +5,22 @@ If you haven't already, join our [Discord channel](https://discord.gg/qgPrHv4)!
|
|||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
Try to keep your pull requests relevant, neat, and manageable. If you are adding multiple features, split them into separate PRs.
|
Try to keep your pull requests relevant, neat, and manageable. If you are adding multiple features, split them into separate PRs.
|
||||||
* Avoid including irellevant whitespace or formatting changes.
|
These are the main points to follow:
|
||||||
* Comment your code.
|
|
||||||
* Spell check your code / comments
|
1) Use formatting which is consistent with the rest of xNode base (see below)
|
||||||
* Use consistent formatting
|
2) Keep _one feature_ per PR (see below)
|
||||||
|
3) xNode aims to be compatible with C# 4.x, do not use new language features
|
||||||
|
4) Avoid including irellevant whitespace or formatting changes
|
||||||
|
5) Comment your code
|
||||||
|
6) Spell check your code / comments
|
||||||
|
7) Use concrete types, not *var*
|
||||||
|
8) Use english language
|
||||||
|
|
||||||
## New features
|
## New features
|
||||||
xNode aims to be simple and extendible, not trying to fix all of Unity's shortcomings.
|
xNode aims to be simple and extendible, not trying to fix all of Unity's shortcomings.
|
||||||
|
|
||||||
|
Approved changes might be rejected if bundled with rejected changes, so keep PRs as separate as possible.
|
||||||
|
|
||||||
If your feature aims to cover something not related to editing nodes, it generally won't be accepted. If in doubt, ask on the Discord channel.
|
If your feature aims to cover something not related to editing nodes, it generally won't be accepted. If in doubt, ask on the Discord channel.
|
||||||
|
|
||||||
## Coding conventions
|
## Coding conventions
|
||||||
|
|||||||
73
Scripts/Editor/GraphAndNodeEditor.cs
Normal file
73
Scripts/Editor/GraphAndNodeEditor.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
#if ODIN_INSPECTOR
|
||||||
|
using Sirenix.OdinInspector.Editor;
|
||||||
|
using Sirenix.Utilities;
|
||||||
|
using Sirenix.Utilities.Editor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace XNodeEditor {
|
||||||
|
/// <summary> Override graph inspector to show an 'Open Graph' button at the top </summary>
|
||||||
|
[CustomEditor(typeof(XNode.NodeGraph), true)]
|
||||||
|
#if ODIN_INSPECTOR
|
||||||
|
public class GlobalGraphEditor : OdinEditor {
|
||||||
|
public override void OnInspectorGUI() {
|
||||||
|
if (GUILayout.Button("Edit graph", GUILayout.Height(40))) {
|
||||||
|
NodeEditorWindow.Open(serializedObject.targetObject as XNode.NodeGraph);
|
||||||
|
}
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public class GlobalGraphEditor : Editor {
|
||||||
|
public override void OnInspectorGUI() {
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Edit graph", GUILayout.Height(40))) {
|
||||||
|
NodeEditorWindow.Open(serializedObject.targetObject as XNode.NodeGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Space(EditorGUIUtility.singleLineHeight);
|
||||||
|
GUILayout.Label("Raw data", "BoldLabel");
|
||||||
|
|
||||||
|
DrawDefaultInspector();
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[CustomEditor(typeof(XNode.Node), true)]
|
||||||
|
#if ODIN_INSPECTOR
|
||||||
|
public class GlobalNodeEditor : OdinEditor {
|
||||||
|
public override void OnInspectorGUI() {
|
||||||
|
if (GUILayout.Button("Edit graph", GUILayout.Height(40))) {
|
||||||
|
SerializedProperty graphProp = serializedObject.FindProperty("graph");
|
||||||
|
NodeEditorWindow w = NodeEditorWindow.Open(graphProp.objectReferenceValue as XNode.NodeGraph);
|
||||||
|
w.Home(); // Focus selected node
|
||||||
|
}
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public class GlobalNodeEditor : Editor {
|
||||||
|
public override void OnInspectorGUI() {
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Edit graph", GUILayout.Height(40))) {
|
||||||
|
SerializedProperty graphProp = serializedObject.FindProperty("graph");
|
||||||
|
NodeEditorWindow w = NodeEditorWindow.Open(graphProp.objectReferenceValue as XNode.NodeGraph);
|
||||||
|
w.Home(); // Focus selected node
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Space(EditorGUIUtility.singleLineHeight);
|
||||||
|
GUILayout.Label("Raw data", "BoldLabel");
|
||||||
|
|
||||||
|
// Now draw the node itself.
|
||||||
|
DrawDefaultInspector();
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
11
Scripts/Editor/GraphAndNodeEditor.cs.meta
Normal file
11
Scripts/Editor/GraphAndNodeEditor.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bdd6e443125ccac4dad0665515759637
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -21,7 +21,7 @@ namespace XNodeEditor {
|
|||||||
public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>();
|
public readonly static Dictionary<XNode.NodePort, Vector2> portPositions = new Dictionary<XNode.NodePort, Vector2>();
|
||||||
|
|
||||||
#if ODIN_INSPECTOR
|
#if ODIN_INSPECTOR
|
||||||
internal static bool inNodeEditor = false;
|
protected internal static bool inNodeEditor = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public virtual void OnHeaderGUI() {
|
public virtual void OnHeaderGUI() {
|
||||||
@ -129,9 +129,14 @@ namespace XNodeEditor {
|
|||||||
public void Rename(string newName) {
|
public void Rename(string newName) {
|
||||||
if (newName == null || newName.Trim() == "") newName = NodeEditorUtilities.NodeDefaultName(target.GetType());
|
if (newName == null || newName.Trim() == "") newName = NodeEditorUtilities.NodeDefaultName(target.GetType());
|
||||||
target.name = newName;
|
target.name = newName;
|
||||||
|
OnRename();
|
||||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Called after this node's name has changed. </summary>
|
||||||
|
public virtual void OnRename() { }
|
||||||
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
public class CustomNodeEditorAttribute : Attribute,
|
public class CustomNodeEditorAttribute : Attribute,
|
||||||
XNodeEditor.Internal.NodeEditorBase<NodeEditor, NodeEditor.CustomNodeEditorAttribute, XNode.Node>.INodeEditorAttrib {
|
XNodeEditor.Internal.NodeEditorBase<NodeEditor, NodeEditor.CustomNodeEditorAttribute, XNode.Node>.INodeEditorAttrib {
|
||||||
|
|||||||
@ -58,10 +58,9 @@ namespace XNodeEditor {
|
|||||||
case EventType.MouseDrag:
|
case EventType.MouseDrag:
|
||||||
if (e.button == 0) {
|
if (e.button == 0) {
|
||||||
if (IsDraggingPort) {
|
if (IsDraggingPort) {
|
||||||
if (IsHoveringPort && hoveredPort.IsInput && draggedOutput.CanConnectTo(hoveredPort)) {
|
// Set target even if we can't connect, so as to prevent auto-conn menu from opening erroneously
|
||||||
if (!draggedOutput.IsConnectedTo(hoveredPort)) {
|
if (IsHoveringPort && hoveredPort.IsInput && !draggedOutput.IsConnectedTo(hoveredPort)) {
|
||||||
draggedOutputTarget = hoveredPort;
|
draggedOutputTarget = hoveredPort;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
draggedOutputTarget = null;
|
draggedOutputTarget = null;
|
||||||
}
|
}
|
||||||
@ -205,8 +204,8 @@ namespace XNodeEditor {
|
|||||||
if (e.button == 0) {
|
if (e.button == 0) {
|
||||||
//Port drag release
|
//Port drag release
|
||||||
if (IsDraggingPort) {
|
if (IsDraggingPort) {
|
||||||
//If connection is valid, save it
|
// If connection is valid, save it
|
||||||
if (draggedOutputTarget != null) {
|
if (draggedOutputTarget != null && draggedOutput.CanConnectTo(draggedOutputTarget)) {
|
||||||
XNode.Node node = draggedOutputTarget.node;
|
XNode.Node node = draggedOutputTarget.node;
|
||||||
if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
|
if (graph.nodes.Count != 0) draggedOutput.Connect(draggedOutputTarget);
|
||||||
|
|
||||||
@ -218,8 +217,8 @@ namespace XNodeEditor {
|
|||||||
EditorUtility.SetDirty(graph);
|
EditorUtility.SetDirty(graph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Open context menu for auto-connection
|
// Open context menu for auto-connection if there is no target node
|
||||||
else if (NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) {
|
else if (draggedOutputTarget == null && NodeEditorPreferences.GetSettings().dragToCreate && autoConnectOutput != null) {
|
||||||
GenericMenu menu = new GenericMenu();
|
GenericMenu menu = new GenericMenu();
|
||||||
graphEditor.AddContextMenuItems(menu);
|
graphEditor.AddContextMenuItems(menu);
|
||||||
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero));
|
||||||
@ -457,8 +456,8 @@ namespace XNodeEditor {
|
|||||||
|
|
||||||
XNode.Node newNodeIn, newNodeOut;
|
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.UpdateStaticPorts();
|
newNodeIn.UpdatePorts();
|
||||||
newNodeOut.UpdateStaticPorts();
|
newNodeOut.UpdatePorts();
|
||||||
inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
|
inputPort = newNodeIn.GetInputPort(inputPort.fieldName);
|
||||||
outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
|
outputPort = newNodeOut.GetOutputPort(outputPort.fieldName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ namespace XNodeEditor {
|
|||||||
}).Where(x => x.port != null);
|
}).Where(x => x.port != null);
|
||||||
List<XNode.NodePort> dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList();
|
List<XNode.NodePort> dynamicPorts = indexedPorts.OrderBy(x => x.index).Select(x => x.port).ToList();
|
||||||
|
|
||||||
|
node.UpdatePorts();
|
||||||
|
|
||||||
ReorderableList list = null;
|
ReorderableList list = null;
|
||||||
Dictionary<string, ReorderableList> rlc;
|
Dictionary<string, ReorderableList> rlc;
|
||||||
if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) {
|
if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) {
|
||||||
@ -326,6 +328,7 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
list.list = dynamicPorts;
|
list.list = dynamicPorts;
|
||||||
list.DoLayoutList();
|
list.DoLayoutList();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReorderableList CreateReorderableList(string fieldName, List<XNode.NodePort> dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action<ReorderableList> onCreation) {
|
private static ReorderableList CreateReorderableList(string fieldName, List<XNode.NodePort> dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action<ReorderableList> onCreation) {
|
||||||
@ -337,7 +340,7 @@ namespace XNodeEditor {
|
|||||||
list.drawElementCallback =
|
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);
|
XNode.NodePort port = node.GetPort(fieldName + " " + index);
|
||||||
if (hasArrayData) {
|
if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) {
|
||||||
if (arrayData.arraySize <= index) {
|
if (arrayData.arraySize <= index) {
|
||||||
EditorGUI.LabelField(rect, "Array[" + index + "] data out of range");
|
EditorGUI.LabelField(rect, "Array[" + index + "] data out of range");
|
||||||
return;
|
return;
|
||||||
@ -465,7 +468,7 @@ namespace XNodeEditor {
|
|||||||
EditorUtility.SetDirty(node);
|
EditorUtility.SetDirty(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasArrayData) {
|
if (hasArrayData && arrayData.propertyType != SerializedPropertyType.String) {
|
||||||
if (arrayData.arraySize <= index) {
|
if (arrayData.arraySize <= index) {
|
||||||
Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped");
|
Debug.LogWarning("Attempted to remove array index " + index + " where only " + arrayData.arraySize + " exist - Skipped");
|
||||||
Debug.Log(rl.list[0]);
|
Debug.Log(rl.list[0]);
|
||||||
|
|||||||
@ -187,12 +187,13 @@ namespace XNodeEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Open the provided graph in the NodeEditor</summary>
|
/// <summary>Open the provided graph in the NodeEditor</summary>
|
||||||
public static void Open(XNode.NodeGraph graph) {
|
public static NodeEditorWindow Open(XNode.NodeGraph graph) {
|
||||||
if (!graph) return;
|
if (!graph) return null;
|
||||||
|
|
||||||
NodeEditorWindow w = GetWindow(typeof(NodeEditorWindow), false, "xNode", true) as NodeEditorWindow;
|
NodeEditorWindow w = GetWindow(typeof(NodeEditorWindow), false, "xNode", true) as NodeEditorWindow;
|
||||||
w.wantsMouseMove = true;
|
w.wantsMouseMove = true;
|
||||||
w.graph = graph;
|
w.graph = graph;
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Repaint all open NodeEditorWindows. </summary>
|
/// <summary> Repaint all open NodeEditorWindows. </summary>
|
||||||
|
|||||||
@ -49,6 +49,7 @@ namespace XNodeEditor {
|
|||||||
if (input == null || input.Trim() == "") {
|
if (input == null || input.Trim() == "") {
|
||||||
if (GUILayout.Button("Revert to default") || (e.isKey && e.keyCode == KeyCode.Return)) {
|
if (GUILayout.Button("Revert to default") || (e.isKey && e.keyCode == KeyCode.Return)) {
|
||||||
target.name = NodeEditorUtilities.NodeDefaultName(target.GetType());
|
target.name = NodeEditorUtilities.NodeDefaultName(target.GetType());
|
||||||
|
NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename();
|
||||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
||||||
Close();
|
Close();
|
||||||
target.TriggerOnValidate();
|
target.TriggerOnValidate();
|
||||||
@ -58,6 +59,7 @@ namespace XNodeEditor {
|
|||||||
else {
|
else {
|
||||||
if (GUILayout.Button("Apply") || (e.isKey && e.keyCode == KeyCode.Return)) {
|
if (GUILayout.Button("Apply") || (e.isKey && e.keyCode == KeyCode.Return)) {
|
||||||
target.name = input;
|
target.name = input;
|
||||||
|
NodeEditor.GetEditor((XNode.Node)target, NodeEditorWindow.current).OnRename();
|
||||||
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target));
|
||||||
Close();
|
Close();
|
||||||
target.TriggerOnValidate();
|
target.TriggerOnValidate();
|
||||||
|
|||||||
@ -119,12 +119,12 @@ namespace XNode {
|
|||||||
protected void OnEnable() {
|
protected void OnEnable() {
|
||||||
if (graphHotfix != null) graph = graphHotfix;
|
if (graphHotfix != null) graph = graphHotfix;
|
||||||
graphHotfix = null;
|
graphHotfix = null;
|
||||||
UpdateStaticPorts();
|
UpdatePorts();
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Update static ports to reflect class fields. This happens automatically on enable. </summary>
|
/// <summary> Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. This happens automatically on enable or on redrawing a dynamic port list. </summary>
|
||||||
public void UpdateStaticPorts() {
|
public void UpdatePorts() {
|
||||||
NodeDataCache.UpdatePorts(this, ports);
|
NodeDataCache.UpdatePorts(this, ports);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ namespace XNode {
|
|||||||
|
|
||||||
#region Attributes
|
#region Attributes
|
||||||
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
|
/// <summary> Mark a serializable field as an input port. You can access this through <see cref="GetInputPort(string)"/> </summary>
|
||||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
public class InputAttribute : Attribute {
|
public class InputAttribute : Attribute {
|
||||||
public ShowBackingValue backingValue;
|
public ShowBackingValue backingValue;
|
||||||
public ConnectionType connectionType;
|
public ConnectionType connectionType;
|
||||||
@ -285,7 +285,7 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Mark a serializable field as an output port. You can access this through <see cref="GetOutputPort(string)"/> </summary>
|
/// <summary> Mark a serializable field as an output port. You can access this through <see cref="GetOutputPort(string)"/> </summary>
|
||||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
public class OutputAttribute : Attribute {
|
public class OutputAttribute : Attribute {
|
||||||
public ShowBackingValue backingValue;
|
public ShowBackingValue backingValue;
|
||||||
public ConnectionType connectionType;
|
public ConnectionType connectionType;
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -9,7 +10,7 @@ namespace XNode {
|
|||||||
private static PortDataCache portDataCache;
|
private static PortDataCache portDataCache;
|
||||||
private static bool Initialized { get { return portDataCache != null; } }
|
private static bool Initialized { get { return portDataCache != null; } }
|
||||||
|
|
||||||
/// <summary> Update static ports to reflect class fields. </summary>
|
/// <summary> Update static ports and dynamic ports managed by DynamicPortLists to reflect class fields. </summary>
|
||||||
public static void UpdatePorts(Node node, Dictionary<string, NodePort> ports) {
|
public static void UpdatePorts(Node node, Dictionary<string, NodePort> ports) {
|
||||||
if (!Initialized) BuildCache();
|
if (!Initialized) BuildCache();
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ namespace XNode {
|
|||||||
Dictionary<string, List<NodePort>> removedPorts = new Dictionary<string, List<NodePort>>();
|
Dictionary<string, List<NodePort>> removedPorts = new Dictionary<string, List<NodePort>>();
|
||||||
System.Type nodeType = node.GetType();
|
System.Type nodeType = node.GetType();
|
||||||
|
|
||||||
|
List<NodePort> dynamicListPorts = new List<NodePort>();
|
||||||
|
|
||||||
List<NodePort> typePortCache;
|
List<NodePort> typePortCache;
|
||||||
if (portDataCache.TryGetValue(nodeType, out typePortCache)) {
|
if (portDataCache.TryGetValue(nodeType, out typePortCache)) {
|
||||||
for (int i = 0; i < typePortCache.Count; i++) {
|
for (int i = 0; i < typePortCache.Count; i++) {
|
||||||
@ -25,6 +28,7 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup port dict - Remove nonexisting static ports - update static port types
|
// Cleanup port dict - Remove nonexisting static ports - update static port types
|
||||||
|
// AND update dynamic ports (albeit only those in lists) too, in order to enforce proper serialisation.
|
||||||
// Loop through current node ports
|
// Loop through current node ports
|
||||||
foreach (NodePort port in ports.Values.ToList()) {
|
foreach (NodePort port in ports.Values.ToList()) {
|
||||||
// If port still exists, check it it has been changed
|
// If port still exists, check it it has been changed
|
||||||
@ -43,6 +47,10 @@ namespace XNode {
|
|||||||
port.ClearConnections();
|
port.ClearConnections();
|
||||||
ports.Remove(port.fieldName);
|
ports.Remove(port.fieldName);
|
||||||
}
|
}
|
||||||
|
// If the port is dynamic and is managed by a dynamic port list, flag it for reference updates
|
||||||
|
else if (IsDynamicListPort(port)) {
|
||||||
|
dynamicListPorts.Add(port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add missing ports
|
// Add missing ports
|
||||||
foreach (NodePort staticPort in staticPorts.Values) {
|
foreach (NodePort staticPort in staticPorts.Values) {
|
||||||
@ -60,6 +68,56 @@ namespace XNode {
|
|||||||
ports.Add(staticPort.fieldName, port);
|
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;
|
||||||
|
listPort.connectionType = backingPort.connectionType;
|
||||||
|
listPort.typeConstraint = backingPort.typeConstraint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the underlying types from arrays and lists, the only serialisable collections which will have
|
||||||
|
/// their elements drawn otherwise. 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.
|
||||||
|
/// </summary>
|
||||||
|
private static Type GetBackingValueType(Type portValType) {
|
||||||
|
if (portValType.HasElementType) {
|
||||||
|
return portValType.GetElementType();
|
||||||
|
}
|
||||||
|
if (portValType.IsGenericType && portValType.GetGenericTypeDefinition() == typeof(List<>)) {
|
||||||
|
return portValType.GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
return portValType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Returns true if the given port is in a dynamic port list.</summary>
|
||||||
|
private static bool IsDynamicListPort(NodePort port) {
|
||||||
|
// Ports flagged as "dynamicPortList = true" end up having a "backing port" and a name with an index, but we have
|
||||||
|
// no guarantee that a dynamic port called "output 0" is an element in a list backed by a static "output" port.
|
||||||
|
// 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;
|
||||||
|
Node.OutputAttribute outputAttribute = x as Node.OutputAttribute;
|
||||||
|
return inputAttribute != null && inputAttribute.dynamicPortList ||
|
||||||
|
outputAttribute != null && outputAttribute.dynamicPortList;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Cache node types </summary>
|
/// <summary> Cache node types </summary>
|
||||||
|
|||||||
@ -19,9 +19,18 @@ namespace XNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IO direction { get { return _direction; } }
|
public IO direction {
|
||||||
public Node.ConnectionType connectionType { get { return _connectionType; } }
|
get { return _direction; }
|
||||||
public Node.TypeConstraint typeConstraint { get { return _typeConstraint; } }
|
internal set { _direction = value; }
|
||||||
|
}
|
||||||
|
public Node.ConnectionType connectionType {
|
||||||
|
get { return _connectionType; }
|
||||||
|
internal set { _connectionType = value; }
|
||||||
|
}
|
||||||
|
public Node.TypeConstraint typeConstraint {
|
||||||
|
get { return _typeConstraint; }
|
||||||
|
internal set { _typeConstraint = value; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Is this port connected to anytihng? </summary>
|
/// <summary> Is this port connected to anytihng? </summary>
|
||||||
public bool IsConnected { get { return connections.Count != 0; } }
|
public bool IsConnected { get { return connections.Count != 0; } }
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user