From d352335a7490bd37ee77325cb2abeee4f5d7de7c Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Wed, 17 Jan 2018 11:15:24 +0100 Subject: [PATCH 01/15] Removed examples from master branch. You can find them on the Examples branch. --- Example.meta | 9 - Example/ExampleNodeGraph.asset | 183 ------------------ Example/ExampleNodeGraph.asset.meta | 10 - Example/ExampleNodeGraph.cs | 8 - Example/ExampleNodeGraph.cs.meta | 12 -- Example/Nodes.meta | 9 - Example/Nodes/DisplayValue.cs | 14 -- Example/Nodes/DisplayValue.cs.meta | 12 -- Example/Nodes/Editor.meta | 10 - Example/Nodes/Editor/DisplayValueEditor.cs | 15 -- .../Nodes/Editor/DisplayValueEditor.cs.meta | 13 -- Example/Nodes/MathNode.cs | 35 ---- Example/Nodes/MathNode.cs.meta | 13 -- Example/Nodes/Vector.cs | 16 -- Example/Nodes/Vector.cs.meta | 13 -- 15 files changed, 372 deletions(-) delete mode 100644 Example.meta delete mode 100644 Example/ExampleNodeGraph.asset delete mode 100644 Example/ExampleNodeGraph.asset.meta delete mode 100644 Example/ExampleNodeGraph.cs delete mode 100644 Example/ExampleNodeGraph.cs.meta delete mode 100644 Example/Nodes.meta delete mode 100644 Example/Nodes/DisplayValue.cs delete mode 100644 Example/Nodes/DisplayValue.cs.meta delete mode 100644 Example/Nodes/Editor.meta delete mode 100644 Example/Nodes/Editor/DisplayValueEditor.cs delete mode 100644 Example/Nodes/Editor/DisplayValueEditor.cs.meta delete mode 100644 Example/Nodes/MathNode.cs delete mode 100644 Example/Nodes/MathNode.cs.meta delete mode 100644 Example/Nodes/Vector.cs delete mode 100644 Example/Nodes/Vector.cs.meta diff --git a/Example.meta b/Example.meta deleted file mode 100644 index a6b543b..0000000 --- a/Example.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 3cfe6eabeed0aa44e8d9d54b308a461f -folderAsset: yes -timeCreated: 1505418316 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/ExampleNodeGraph.asset b/Example/ExampleNodeGraph.asset deleted file mode 100644 index a50d83c..0000000 --- a/Example/ExampleNodeGraph.asset +++ /dev/null @@ -1,183 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: a6399826e2c44b447b32a3ed06646162, type: 3} - m_Name: ExampleNodeGraph - m_EditorClassIdentifier: - nodes: - - {fileID: 114708853913061688} - - {fileID: 114511978881715272} - - {fileID: 114509033286994848} - - {fileID: 114245496101350052} ---- !u!114 &114245496101350052 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 98f6f901f0da53142b79277ea3f42518, type: 3} - m_Name: DisplayValue - m_EditorClassIdentifier: - graph: {fileID: 11400000} - position: {x: -168, y: 8} - ports: - keys: - - input - values: - - _fieldName: input - _node: {fileID: 114245496101350052} - _typeQualifiedName: System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: - - fieldName: result - node: {fileID: 114511978881715272} - _direction: 0 - _connectionType: 1 - _dynamic: 1 ---- !u!114 &114509033286994848 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 98f6f901f0da53142b79277ea3f42518, type: 3} - m_Name: DisplayValue - m_EditorClassIdentifier: - graph: {fileID: 11400000} - position: {x: 72, y: -72} - ports: - keys: - - input - values: - - _fieldName: input - _node: {fileID: 114509033286994848} - _typeQualifiedName: System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: - - fieldName: vector - node: {fileID: 114708853913061688} - _direction: 0 - _connectionType: 1 - _dynamic: 1 ---- !u!114 &114511978881715272 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 19e541bba2a188f4a84c6f3718ee6d55, type: 3} - m_Name: MathNode - m_EditorClassIdentifier: - graph: {fileID: 11400000} - position: {x: -472, y: -120} - ports: - keys: - - a - - b - - result - values: - - _fieldName: a - _node: {fileID: 114511978881715272} - _typeQualifiedName: System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: [] - _direction: 0 - _connectionType: 0 - _dynamic: 0 - - _fieldName: b - _node: {fileID: 114511978881715272} - _typeQualifiedName: System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: [] - _direction: 0 - _connectionType: 0 - _dynamic: 0 - - _fieldName: result - _node: {fileID: 114511978881715272} - _typeQualifiedName: System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: - - fieldName: x - node: {fileID: 114708853913061688} - - fieldName: input - node: {fileID: 114245496101350052} - _direction: 1 - _connectionType: 0 - _dynamic: 0 - a: 6.48 - b: 7.59 - result: 14.07 - mathType: 0 ---- !u!114 &114708853913061688 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 05559f4106850df4ab41776666216480, type: 3} - m_Name: Vector - m_EditorClassIdentifier: - graph: {fileID: 11400000} - position: {x: -168, y: -120} - ports: - keys: - - x - - y - - z - - vector - values: - - _fieldName: x - _node: {fileID: 114708853913061688} - _typeQualifiedName: System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: - - fieldName: result - node: {fileID: 114511978881715272} - _direction: 0 - _connectionType: 0 - _dynamic: 0 - - _fieldName: y - _node: {fileID: 114708853913061688} - _typeQualifiedName: System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: [] - _direction: 0 - _connectionType: 0 - _dynamic: 0 - - _fieldName: z - _node: {fileID: 114708853913061688} - _typeQualifiedName: System.Single, mscorlib, Version=2.0.0.0, Culture=neutral, - PublicKeyToken=b77a5c561934e089 - connections: [] - _direction: 0 - _connectionType: 0 - _dynamic: 0 - - _fieldName: vector - _node: {fileID: 114708853913061688} - _typeQualifiedName: UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0, - Culture=neutral, PublicKeyToken=null - connections: - - fieldName: input - node: {fileID: 114509033286994848} - _direction: 1 - _connectionType: 0 - _dynamic: 0 - x: 0 - y: 2.6412349 - z: 14.33477 - vector: {x: 14.07, y: 2.6412349, z: 14.33477} diff --git a/Example/ExampleNodeGraph.asset.meta b/Example/ExampleNodeGraph.asset.meta deleted file mode 100644 index 0ef23bc..0000000 --- a/Example/ExampleNodeGraph.asset.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: cee47fa32ae90bb4f8f0bec6f186cb3b -timeCreated: 1509308927 -licenseType: Free -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/ExampleNodeGraph.cs b/Example/ExampleNodeGraph.cs deleted file mode 100644 index c9a7fa1..0000000 --- a/Example/ExampleNodeGraph.cs +++ /dev/null @@ -1,8 +0,0 @@ -using UnityEngine; -using System; -using XNode; - -/// Defines an example nodegraph. -[Serializable, CreateAssetMenu(fileName = "ExampleNodeGraph", menuName = "Node Graph/Example")] -public class ExampleNodeGraph : XNode.NodeGraph { -} diff --git a/Example/ExampleNodeGraph.cs.meta b/Example/ExampleNodeGraph.cs.meta deleted file mode 100644 index 43c9d35..0000000 --- a/Example/ExampleNodeGraph.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: a6399826e2c44b447b32a3ed06646162 -timeCreated: 1506460823 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/Nodes.meta b/Example/Nodes.meta deleted file mode 100644 index 0d29f0a..0000000 --- a/Example/Nodes.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 31c1681f5df4f764ab4ca7f09cd3be7d -folderAsset: yes -timeCreated: 1505462700 -licenseType: Free -DefaultImporter: - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/Nodes/DisplayValue.cs b/Example/Nodes/DisplayValue.cs deleted file mode 100644 index 3f5a317..0000000 --- a/Example/Nodes/DisplayValue.cs +++ /dev/null @@ -1,14 +0,0 @@ -using XNode; - -namespace BasicNodes { - public class DisplayValue : XNode.Node { - protected override void Init() { - base.Init(); - if (!HasPort("input")) AddInstanceInput(typeof(object), ConnectionType.Override ,"input"); - } - - public override object GetValue(XNode.NodePort port) { - return GetInputValue("input"); - } - } -} diff --git a/Example/Nodes/DisplayValue.cs.meta b/Example/Nodes/DisplayValue.cs.meta deleted file mode 100644 index aa380ea..0000000 --- a/Example/Nodes/DisplayValue.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 98f6f901f0da53142b79277ea3f42518 -timeCreated: 1507499149 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/Nodes/Editor.meta b/Example/Nodes/Editor.meta deleted file mode 100644 index 1008817..0000000 --- a/Example/Nodes/Editor.meta +++ /dev/null @@ -1,10 +0,0 @@ -fileFormatVersion: 2 -guid: 0d2300267781fed46a6d964565309cbf -folderAsset: yes -timeCreated: 1509307735 -licenseType: Free -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/Nodes/Editor/DisplayValueEditor.cs b/Example/Nodes/Editor/DisplayValueEditor.cs deleted file mode 100644 index c7482c4..0000000 --- a/Example/Nodes/Editor/DisplayValueEditor.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UnityEditor; -using XNodeEditor; - -namespace BasicNodes { - [CustomNodeEditor(typeof(DisplayValue))] - public class DisplayValueEditor : NodeEditor { - - public override void OnBodyGUI() { - base.OnBodyGUI(); - NodeEditorGUILayout.PortField(target.GetInputPort("input")); - object obj = target.GetValue(null); - if (obj != null) EditorGUILayout.LabelField(obj.ToString()); - } - } -} \ No newline at end of file diff --git a/Example/Nodes/Editor/DisplayValueEditor.cs.meta b/Example/Nodes/Editor/DisplayValueEditor.cs.meta deleted file mode 100644 index 971da18..0000000 --- a/Example/Nodes/Editor/DisplayValueEditor.cs.meta +++ /dev/null @@ -1,13 +0,0 @@ -fileFormatVersion: 2 -guid: 7d7298690665789498dc42a285eb2c28 -timeCreated: 1509305659 -licenseType: Free -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/Nodes/MathNode.cs b/Example/Nodes/MathNode.cs deleted file mode 100644 index b9c1e83..0000000 --- a/Example/Nodes/MathNode.cs +++ /dev/null @@ -1,35 +0,0 @@ -using XNode; - -namespace BasicNodes { - [System.Serializable] - public class MathNode : XNode.Node { - // Adding [Input] or [Output] is all you need to do to register a field as a valid port on your node - [Input] public float a; - [Input] public float b; - // The value of an output node field is not used for anything, but could be used for caching output results - [Output] public float result; - - // Will be displayed as an editable field - just like the normal inspector - public MathType mathType = MathType.Add; - public enum MathType { Add, Subtract, Multiply, Divide } - - // GetValue should be overridden to return a value for any specified output port - public override object GetValue(XNode.NodePort port) { - - // Get new a and b values from input connections. Fallback to field values if input is not connected - float a = GetInputValue("a", this.a); - float b = GetInputValue("b", this.b); - - // After you've gotten your input values, you can perform your calculations and return a value - result = 0f; - if (port.fieldName == "result") - switch (mathType) { - case MathType.Add: default: result = a+b; break; - case MathType.Subtract: result = a - b; break; - case MathType.Multiply: result = a * b; break; - case MathType.Divide: result = a / b; break; - } - return result; - } - } -} \ No newline at end of file diff --git a/Example/Nodes/MathNode.cs.meta b/Example/Nodes/MathNode.cs.meta deleted file mode 100644 index bd2894c..0000000 --- a/Example/Nodes/MathNode.cs.meta +++ /dev/null @@ -1,13 +0,0 @@ -fileFormatVersion: 2 -guid: 19e541bba2a188f4a84c6f3718ee6d55 -timeCreated: 1509307779 -licenseType: Free -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Example/Nodes/Vector.cs b/Example/Nodes/Vector.cs deleted file mode 100644 index 7141d2e..0000000 --- a/Example/Nodes/Vector.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UnityEngine; -using XNode; - -namespace BasicNodes { - public class Vector : XNode.Node { - [Input] public float x, y, z; - [Output] public Vector3 vector; - - public override object GetValue(XNode.NodePort port) { - vector.x = GetInputValue("x", this.x); - vector.y = GetInputValue("y", this.y); - vector.z = GetInputValue("z", this.z); - return vector; - } - } -} \ No newline at end of file diff --git a/Example/Nodes/Vector.cs.meta b/Example/Nodes/Vector.cs.meta deleted file mode 100644 index 968b1f6..0000000 --- a/Example/Nodes/Vector.cs.meta +++ /dev/null @@ -1,13 +0,0 @@ -fileFormatVersion: 2 -guid: 05559f4106850df4ab41776666216480 -timeCreated: 1509303406 -licenseType: Free -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From aa549506119ae9a82cf4f4ba95f3f1c08fe5bd19 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sun, 21 Jan 2018 11:20:23 +0100 Subject: [PATCH 02/15] Fixed minor bug (error message) --- Scripts/NodeDataCache.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index d37f31f..262ef04 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -63,7 +63,7 @@ namespace XNode { if (inputAttrib == null && outputAttrib == null) continue; - if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo + " cannot be both input and output."); + if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo[i].Name + " cannot be both input and output."); else { if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List()); portDataCache[nodeType].Add(new NodePort(fieldInfo[i])); @@ -98,4 +98,4 @@ namespace XNode { } } } -} \ No newline at end of file +} From 55fb7586d8f5ea9cfd3de0e0ab63e90ce44af9a2 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sun, 21 Jan 2018 22:45:20 +0100 Subject: [PATCH 03/15] Register ConnectionType changes and update existing static NodePorts --- Scripts/NodeDataCache.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index 262ef04..d6b7062 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -23,12 +23,17 @@ namespace XNode { } // Cleanup port dict - Remove nonexisting static ports - update static port types + // Loop through current node ports foreach (NodePort port in ports.Values.ToList()) { + // If port still exists, check it it has been changed if (staticPorts.ContainsKey(port.fieldName)) { NodePort staticPort = staticPorts[port.fieldName]; - if (port.IsDynamic || port.direction != staticPort.direction) ports.Remove(port.fieldName); + // If port exists but with wrong settings, remove it. Re-add it later. + if (port.connectionType != staticPort.connectionType || port.IsDynamic || port.direction != staticPort.direction) ports.Remove(port.fieldName); else port.ValueType = staticPort.ValueType; - } else if (port.IsStatic) ports.Remove(port.fieldName); + } + // If port doesn't exist anymore, remove it + else if (port.IsStatic) ports.Remove(port.fieldName); } // Add missing ports foreach (NodePort staticPort in staticPorts.Values) { From 2651bfbeb5350e7a1a5a57248df45b613094fee3 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Sun, 21 Jan 2018 23:55:55 +0100 Subject: [PATCH 04/15] Increase crispyness of UI text --- Scripts/Editor/NodeEditorAction.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 3a70848..409eea8 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -54,7 +54,12 @@ namespace XNodeEditor { Repaint(); } } else if (e.button == 1 || e.button == 2) { - panOffset += e.delta * zoom; + Vector2 tempOffset = panOffset; + tempOffset += e.delta * zoom; + // Round value to increase crispyness of UI text + tempOffset.x = Mathf.Round(tempOffset.x); + tempOffset.y = Mathf.Round(tempOffset.y); + panOffset = tempOffset; isPanning = true; } break; From fcbd48b0bef4527f2256d80b5e7901619fc25bf1 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 22 Jan 2018 01:19:23 +0100 Subject: [PATCH 05/15] Improved error message --- Scripts/NodeDataCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index d6b7062..acffe69 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -68,7 +68,7 @@ namespace XNode { if (inputAttrib == null && outputAttrib == null) continue; - if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo[i].Name + " cannot be both input and output."); + if (inputAttrib != null && outputAttrib != null) Debug.LogError("Field " + fieldInfo[i].Name + " of type " + nodeType.FullName + " cannot be both input and output."); else { if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List()); portDataCache[nodeType].Add(new NodePort(fieldInfo[i])); From 5d113de554baed378d8c7c64ef44b20ebd177932 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 22 Jan 2018 18:06:50 +0100 Subject: [PATCH 06/15] Added multinode selection --- Scripts/Editor/NodeEditorAction.cs | 87 ++++++++++++++---- Scripts/Editor/NodeEditorGUI.cs | 76 ++++++++++----- Scripts/Editor/NodeEditorResources.cs | 12 ++- Scripts/Editor/NodeEditorWindow.cs | 14 ++- .../Editor/Resources/xnode_node_highlight.png | Bin 0 -> 20433 bytes .../Resources/xnode_node_highlight.png.meta | 87 ++++++++++++++++++ 6 files changed, 228 insertions(+), 48 deletions(-) create mode 100644 Scripts/Editor/Resources/xnode_node_highlight.png create mode 100644 Scripts/Editor/Resources/xnode_node_highlight.png.meta diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 409eea8..6d0cc7c 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -6,22 +6,20 @@ namespace XNodeEditor { public partial class NodeEditorWindow { public static bool isPanning { get; private set; } - public static Vector2 dragOffset; + public static Vector2[] dragOffset; - private bool IsDraggingNode { get { return draggedNode != null; } } + //private bool IsDraggingNode { get { return draggedNode != null; } } private bool IsDraggingPort { get { return draggedOutput != null; } } private bool IsHoveringPort { get { return hoveredPort != null; } } private bool IsHoveringNode { get { return hoveredNode != null; } } - private bool HasSelectedNode { get { return selectedNode != null; } } - + public bool CanDragNodeHeader { get; private set; } + public bool DidDragNodeHeader { get; private set; } private XNode.Node hoveredNode = null; - [NonSerialized] private XNode.Node selectedNode = null; - [NonSerialized] private XNode.Node draggedNode = null; + //[NonSerialized] private XNode.Node draggedNode = null; [NonSerialized] private XNode.NodePort hoveredPort = null; [NonSerialized] private XNode.NodePort draggedOutput = null; [NonSerialized] private XNode.NodePort draggedOutputTarget = null; - private Rect nodeRects; public void Controls() { @@ -45,12 +43,18 @@ namespace XNodeEditor { draggedOutputTarget = null; } Repaint(); - } else if (IsDraggingNode) { - draggedNode.position = WindowToGridPosition(e.mousePosition) + dragOffset; - if (NodeEditorPreferences.gridSnap) { - draggedNode.position.x = (Mathf.Round((draggedNode.position.x + 8) / 16) * 16) - 8; - draggedNode.position.y = (Mathf.Round((draggedNode.position.y + 8) / 16) * 16) - 8; + } else if (CanDragNodeHeader) { + 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; + node.position = WindowToGridPosition(e.mousePosition) + dragOffset[i]; + if (NodeEditorPreferences.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; + } + } } + DidDragNodeHeader = true; Repaint(); } } else if (e.button == 1 || e.button == 2) { @@ -66,7 +70,11 @@ namespace XNodeEditor { case EventType.MouseDown: Repaint(); if (e.button == 0) { - SelectNode(hoveredNode); + + if (hoveredNode == null) Selection.activeObject = null; + else if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, e.control || e.shift); + else if (e.control || e.shift) DeselectNode(hoveredNode); + if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; @@ -82,8 +90,15 @@ namespace XNodeEditor { } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { - draggedNode = hoveredNode; - dragOffset = hoveredNode.position - WindowToGridPosition(e.mousePosition); + DidDragNodeHeader = false; + CanDragNodeHeader = true; + dragOffset = new Vector2[Selection.objects.Length]; + for (int i = 0; i < dragOffset.Length; i++) { + if (Selection.objects[i] is XNode.Node) { + XNode.Node node = Selection.objects[i] as XNode.Node; + dragOffset[i] = node.position - WindowToGridPosition(e.mousePosition); + } + } } } break; @@ -104,16 +119,22 @@ namespace XNodeEditor { EditorUtility.SetDirty(graph); Repaint(); AssetDatabase.SaveAssets(); - } else if (IsDraggingNode) { - draggedNode = null; + } else if (CanDragNodeHeader) { + CanDragNodeHeader = false; AssetDatabase.SaveAssets(); } else if (GUIUtility.hotControl != 0) { AssetDatabase.SaveAssets(); } + + if (IsHoveringNode && !DidDragNodeHeader && !e.control) { + SelectNode(hoveredNode, false); + Repaint(); + } } else if (e.button == 1) { if (!isPanning) { if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { - ShowNodeContextMenu(hoveredNode); + if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode,false); + ShowNodeContextMenu(); } else if (!IsHoveringNode) { ShowGraphContextMenu(); } @@ -121,6 +142,11 @@ namespace XNodeEditor { isPanning = false; } break; + case EventType.KeyDown: + if (e.keyCode == KeyCode.Delete) RemoveSelectedNodes(); + else if (e.keyCode == KeyCode.D && e.control) DublicateSelectedNodes(); + Repaint(); + break; } } @@ -136,6 +162,31 @@ namespace XNodeEditor { Repaint(); } + /// Remove nodes in the graph in Selection.objects + public void RemoveSelectedNodes() { + foreach (UnityEngine.Object item in Selection.objects) { + if (item is XNode.Node) { + XNode.Node node = item as XNode.Node; + graph.RemoveNode(node); + } + } + } + + // Dublicate selected nodes and select the dublicates + public void DublicateSelectedNodes() { + UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length]; + 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; + if (node.graph != graph) continue; // ignore nodes selected in another graph + XNode.Node n = graph.CopyNode(node); + n.position = node.position + new Vector2(30, 30); + newNodes[i] = n; + } + } + Selection.objects = newNodes; + } + /// Draw a connection as we are dragging it public void DrawDraggedConnection() { if (IsDraggingPort) { diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index f8ab6d5..0153965 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -6,7 +6,8 @@ using UnityEngine; namespace XNodeEditor { /// Contains GUI methods public partial class NodeEditorWindow { - NodeGraphEditor currentGraphEditor; + private NodeGraphEditor currentGraphEditor; + private List selectionCache; private void OnGUI() { Event e = Event.current; @@ -74,23 +75,30 @@ namespace XNodeEditor { return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width)); } - /// Show right-click context menu for a node - public void ShowNodeContextMenu(XNode.Node node) { + /// Show right-click context menu for selected nodes + public void ShowNodeContextMenu() { GenericMenu contextMenu = new GenericMenu(); - contextMenu.AddItem(new GUIContent("Move To Top"), false, () => { - int index; - while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { - graph.nodes[index] = graph.nodes[index + 1]; - graph.nodes[index + 1] = node; - } - }); - contextMenu.AddItem(new GUIContent("Duplicate"), false, () => { - XNode.Node n = graph.CopyNode(node); - n.position = node.position + new Vector2(30, 30); - }); - contextMenu.AddItem(new GUIContent("Remove"), false, () => graph.RemoveNode(node)); + // If only one node is selected + if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { + XNode.Node node = Selection.activeObject as XNode.Node; + contextMenu.AddItem(new GUIContent("Move To Top"), false, () => { + int index; + while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { + graph.nodes[index] = graph.nodes[index + 1]; + graph.nodes[index + 1] = node; + } + }); + } + + contextMenu.AddItem(new GUIContent("Duplicate"), false, DublicateSelectedNodes); + contextMenu.AddItem(new GUIContent("Remove"), false, RemoveSelectedNodes); + + // If only one node is selected + if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { + XNode.Node node = Selection.activeObject as XNode.Node; + AddCustomContextMenuItems(contextMenu, node); + } - AddCustomContextMenuItems(contextMenu, node); contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } @@ -168,17 +176,20 @@ namespace XNodeEditor { private void DrawNodes() { Event e = Event.current; + if (e.type == EventType.Layout) { + selectionCache = new List(Selection.objects); + } if (e.type == EventType.Repaint) { portConnectionPoints.Clear(); nodeWidths.Clear(); } - //Selected node is hashed before and after node GUI to detect changes + //Active node is hashed before and after node GUI to detect changes int nodeHash = 0; System.Reflection.MethodInfo onValidate = null; - if (selectedNode != null) { - onValidate = selectedNode.GetType().GetMethod("OnValidate"); - if (onValidate != null) nodeHash = selectedNode.GetHashCode(); + if (Selection.activeObject != null && Selection.activeObject is XNode.Node) { + onValidate = Selection.activeObject.GetType().GetMethod("OnValidate"); + if (onValidate != null) nodeHash = Selection.activeObject.GetHashCode(); } BeginZoomed(position, zoom); @@ -206,9 +217,23 @@ namespace XNodeEditor { GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000))); - GUIStyle style = NodeEditorResources.styles.nodeBody; - GUI.color = nodeEditor.GetTint(); - GUILayout.BeginVertical(new GUIStyle(style)); + bool selected = selectionCache.Contains(graph.nodes[n]); + + if (selected) { + GUIStyle style = new GUIStyle(NodeEditorResources.styles.nodeBody); + GUIStyle highlightStyle = new GUIStyle(NodeEditorResources.styles.nodeHighlight); + highlightStyle.padding = style.padding; + style.padding = new RectOffset(); + GUI.color = nodeEditor.GetTint(); + GUILayout.BeginVertical(new GUIStyle(style)); + GUI.color = Color.white; + GUILayout.BeginVertical(new GUIStyle(highlightStyle)); + } else { + GUIStyle style = NodeEditorResources.styles.nodeBody; + GUI.color = nodeEditor.GetTint(); + GUILayout.BeginVertical(new GUIStyle(style)); + } + GUI.color = guiColor; EditorGUI.BeginChangeCheck(); @@ -235,6 +260,7 @@ namespace XNodeEditor { } GUILayout.EndVertical(); + if (selected) GUILayout.EndVertical(); if (e.type != EventType.Layout) { //Check if we are hovering this node @@ -267,8 +293,8 @@ namespace XNodeEditor { //If a change in hash is detected in the selected node, call OnValidate method. //This is done through reflection because OnValidate is only relevant in editor, //and thus, the code should not be included in build. - if (selectedNode != null) { - if (onValidate != null && nodeHash != selectedNode.GetHashCode()) onValidate.Invoke(selectedNode, null); + if (nodeHash != 0) { + if (onValidate != null && nodeHash != Selection.activeObject.GetHashCode()) onValidate.Invoke(Selection.activeObject, null); } } diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index 490743b..9b55c8f 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -9,13 +9,15 @@ namespace XNodeEditor { private static Texture2D _dotOuter; public static Texture2D nodeBody { get { return _nodeBody != null ? _nodeBody : _nodeBody = Resources.Load("xnode_node"); } } private static Texture2D _nodeBody; + public static Texture2D nodeHighlight { get { return _nodeHighlight != null ? _nodeHighlight : _nodeHighlight = Resources.Load("xnode_node_highlight"); } } + private static Texture2D _nodeHighlight; // Styles public static Styles styles { get { return _styles != null ? _styles : _styles = new Styles(); } } public static Styles _styles = null; public class Styles { - public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip; + public GUIStyle inputPort, outputPort, nodeHeader, nodeBody, tooltip, nodeHighlight; public Styles() { GUIStyle baseStyle = new GUIStyle("Label"); @@ -39,6 +41,10 @@ namespace XNodeEditor { nodeBody.border = new RectOffset(32, 32, 32, 32); nodeBody.padding = new RectOffset(16, 16, 4, 16); + nodeHighlight = new GUIStyle(); + nodeHighlight.normal.background = NodeEditorResources.nodeHighlight; + nodeHighlight.border = new RectOffset(32, 32, 32, 32); + tooltip = new GUIStyle("helpBox"); tooltip.alignment = TextAnchor.MiddleCenter; } @@ -50,8 +56,8 @@ namespace XNodeEditor { for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { Color col = bg; - if (y % 16 == 0 || x % 16 == 0) col = Color.Lerp(line,bg,0.65f); - if (y == 63 || x == 63) col = Color.Lerp(line,bg,0.35f); + if (y % 16 == 0 || x % 16 == 0) col = Color.Lerp(line, bg, 0.65f); + if (y == 63 || x == 63) col = Color.Lerp(line, bg, 0.35f); cols[(y * 64) + x] = col; } } diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 3c79e71..7761a8f 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -77,8 +77,18 @@ namespace XNodeEditor { return new Vector2(xOffset, yOffset); } - public void SelectNode(XNode.Node node) { - selectedNode = node; + public void SelectNode(XNode.Node node, bool add) { + if (add) { + List selection = new List(Selection.objects); + selection.Add(node); + Selection.objects = selection.ToArray(); + } else Selection.activeObject = node; + } + + public void DeselectNode(XNode.Node node) { + List selection = new List(Selection.objects); + selection.Remove(node); + Selection.objects = selection.ToArray(); } [OnOpenAsset(0)] diff --git a/Scripts/Editor/Resources/xnode_node_highlight.png b/Scripts/Editor/Resources/xnode_node_highlight.png new file mode 100644 index 0000000000000000000000000000000000000000..f1bb27f7ff99d83ad32c177e417bab7884ea956e GIT binary patch literal 20433 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 MSz%pd<+tsB0L%jEQ~&?~ literal 0 HcmV?d00001 diff --git a/Scripts/Editor/Resources/xnode_node_highlight.png.meta b/Scripts/Editor/Resources/xnode_node_highlight.png.meta new file mode 100644 index 0000000..21b6034 --- /dev/null +++ b/Scripts/Editor/Resources/xnode_node_highlight.png.meta @@ -0,0 +1,87 @@ +fileFormatVersion: 2 +guid: 2ab2b92d7e1771b47bba0a46a6f0f6d5 +timeCreated: 1516610730 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: From 4e7d844ae5238a536c7395e2cdda5ce6813155cf Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Mon, 22 Jan 2018 18:10:52 +0100 Subject: [PATCH 07/15] Fixed minor multiselect bug --- Scripts/Editor/NodeEditorAction.cs | 2 +- Scripts/Editor/NodeEditorWindow.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 6d0cc7c..a90f5be 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -126,7 +126,7 @@ namespace XNodeEditor { AssetDatabase.SaveAssets(); } - if (IsHoveringNode && !DidDragNodeHeader && !e.control) { + if (IsHoveringNode && !DidDragNodeHeader && !(e.control || e.shift)) { SelectNode(hoveredNode, false); Repaint(); } diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 7761a8f..cf35616 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -82,7 +82,7 @@ namespace XNodeEditor { List selection = new List(Selection.objects); selection.Add(node); Selection.objects = selection.ToArray(); - } else Selection.activeObject = node; + } else Selection.objects = new Object[] { node }; } public void DeselectNode(XNode.Node node) { From 51746307b1ef6ffc16b01aa04bf37b5b4fac6886 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 23 Jan 2018 12:32:59 +0100 Subject: [PATCH 08/15] Documentation corrections --- Scripts/Node.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 2c2ff9b..c1883d3 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -35,9 +35,9 @@ namespace XNode { } public enum ConnectionType { - /// Allow multiple connections + /// Allow multiple connections Multiple, - /// always override the current connection + /// always override the current connection Override, } @@ -200,13 +200,13 @@ namespace XNode { return JsonUtility.ToJson(this).GetHashCode(); } - /// Mark a serializable field as an input port. You can access this through + /// Mark a serializable field as an input port. You can access this through [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] public class InputAttribute : Attribute { public ShowBackingValue backingValue; public ConnectionType connectionType; - /// Mark a serializable field as an input port. You can access this through + /// Mark a serializable field as an input port. You can access this through /// Should we display the backing value for this port as an editor field? /// Should we allow multiple connections? public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected, ConnectionType connectionType = ConnectionType.Multiple) { @@ -215,13 +215,13 @@ namespace XNode { } } - /// Mark a serializable field as an output port. You can access this through + /// Mark a serializable field as an output port. You can access this through [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] public class OutputAttribute : Attribute { public ShowBackingValue backingValue; public ConnectionType connectionType; - /// Mark a serializable field as an output port. You can access this through + /// Mark a serializable field as an output port. You can access this through /// Should we display the backing value for this port as an editor field? /// Should we allow multiple connections? public OutputAttribute(ShowBackingValue backingValue = ShowBackingValue.Never, ConnectionType connectionType = ConnectionType.Multiple) { From a09cda182719a35851accbc50ea1b11845f9f753 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Tue, 23 Jan 2018 18:08:39 +0100 Subject: [PATCH 09/15] Block clicks from registering through node headers --- Scripts/Editor/NodeEditorAction.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index a90f5be..a6ecb8a 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -90,6 +90,7 @@ namespace XNodeEditor { } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { + e.Use(); DidDragNodeHeader = false; CanDragNodeHeader = true; dragOffset = new Vector2[Selection.objects.Length]; From f87af8d62f5636025c56ba4562e67d8a6bc22f67 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Wed, 24 Jan 2018 09:44:55 +0100 Subject: [PATCH 10/15] Changed node selection to only react on header clicks --- Scripts/Editor/NodeEditorAction.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index a6ecb8a..e3499a9 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -71,10 +71,6 @@ namespace XNodeEditor { Repaint(); if (e.button == 0) { - if (hoveredNode == null) Selection.activeObject = null; - else if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, e.control || e.shift); - else if (e.control || e.shift) DeselectNode(hoveredNode); - if (IsHoveringPort) { if (hoveredPort.IsOutput) { draggedOutput = hoveredPort; @@ -90,6 +86,9 @@ namespace XNodeEditor { } } } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { + // If mousedown on node header, select or deselect + if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, e.control || e.shift); + else if (e.control || e.shift) DeselectNode(hoveredNode); e.Use(); DidDragNodeHeader = false; CanDragNodeHeader = true; @@ -101,6 +100,10 @@ namespace XNodeEditor { } } } + // If mousedown on grid background, deselect all + else if (!IsHoveringNode) { + Selection.activeObject = null; + } } break; case EventType.MouseUp: @@ -123,11 +126,18 @@ namespace XNodeEditor { } else if (CanDragNodeHeader) { CanDragNodeHeader = false; AssetDatabase.SaveAssets(); - } else if (GUIUtility.hotControl != 0) { + } else if (!IsHoveringNode) { + // If click outside node, release field focus + if (!isPanning) { + GUIUtility.hotControl = 0; + GUIUtility.keyboardControl = 0; + } + Repaint(); AssetDatabase.SaveAssets(); } - if (IsHoveringNode && !DidDragNodeHeader && !(e.control || e.shift)) { + // If click node header, select single node. + if (IsHoveringNode && !DidDragNodeHeader && IsHoveringTitle(hoveredNode) && !(e.control || e.shift)) { SelectNode(hoveredNode, false); Repaint(); } From d871b7bec44aeea0e3f2e8b08a44a934ce4591b1 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Wed, 24 Jan 2018 12:23:12 +0100 Subject: [PATCH 11/15] Added node box selection --- Scripts/Editor/NodeEditorAction.cs | 50 +++++++++++++++++++----------- Scripts/Editor/NodeEditorGUI.cs | 28 +++++++++++++++++ 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index e3499a9..6e5e035 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -4,23 +4,21 @@ using UnityEngine; namespace XNodeEditor { public partial class NodeEditorWindow { - + public enum NodeActivity { Idle, HoldHeader, DragHeader, HoldGrid, DragGrid } + public static NodeActivity currentActivity = NodeActivity.Idle; public static bool isPanning { get; private set; } public static Vector2[] dragOffset; - //private bool IsDraggingNode { get { return draggedNode != null; } } private bool IsDraggingPort { get { return draggedOutput != null; } } private bool IsHoveringPort { get { return hoveredPort != null; } } private bool IsHoveringNode { get { return hoveredNode != null; } } - public bool CanDragNodeHeader { get; private set; } - public bool DidDragNodeHeader { get; private set; } private XNode.Node hoveredNode = null; - - //[NonSerialized] private XNode.Node draggedNode = null; [NonSerialized] private XNode.NodePort hoveredPort = null; [NonSerialized] private XNode.NodePort draggedOutput = null; [NonSerialized] private XNode.NodePort draggedOutputTarget = null; private Rect nodeRects; + private Vector2 dragBoxStart; + private UnityEngine.Object[] preBoxSelection; public void Controls() { wantsMouseMove = true; @@ -43,7 +41,7 @@ namespace XNodeEditor { draggedOutputTarget = null; } Repaint(); - } else if (CanDragNodeHeader) { + } else if (currentActivity == NodeActivity.HoldHeader || currentActivity == NodeActivity.DragHeader) { 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; @@ -54,7 +52,17 @@ namespace XNodeEditor { } } } - DidDragNodeHeader = true; + currentActivity = NodeActivity.DragHeader; + Repaint(); + } else if (currentActivity == NodeActivity.HoldGrid) { + currentActivity = NodeActivity.DragGrid; + preBoxSelection = Selection.objects; + dragBoxStart = WindowToGridPosition(e.mousePosition); + Repaint(); + } else if (currentActivity == NodeActivity.DragGrid) { + foreach (XNode.Node node in graph.nodes) { + + } Repaint(); } } else if (e.button == 1 || e.button == 2) { @@ -90,8 +98,7 @@ namespace XNodeEditor { if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, e.control || e.shift); else if (e.control || e.shift) DeselectNode(hoveredNode); e.Use(); - DidDragNodeHeader = false; - CanDragNodeHeader = true; + currentActivity = NodeActivity.HoldHeader; dragOffset = new Vector2[Selection.objects.Length]; for (int i = 0; i < dragOffset.Length; i++) { if (Selection.objects[i] is XNode.Node) { @@ -102,7 +109,8 @@ namespace XNodeEditor { } // If mousedown on grid background, deselect all else if (!IsHoveringNode) { - Selection.activeObject = null; + currentActivity = NodeActivity.HoldGrid; + if (!e.control && !e.shift) Selection.activeObject = null; } } break; @@ -121,10 +129,8 @@ namespace XNodeEditor { draggedOutput = null; draggedOutputTarget = null; EditorUtility.SetDirty(graph); - Repaint(); AssetDatabase.SaveAssets(); - } else if (CanDragNodeHeader) { - CanDragNodeHeader = false; + } else if (currentActivity == NodeActivity.DragHeader) { AssetDatabase.SaveAssets(); } else if (!IsHoveringNode) { // If click outside node, release field focus @@ -132,19 +138,20 @@ namespace XNodeEditor { GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; } - Repaint(); AssetDatabase.SaveAssets(); } // If click node header, select single node. - if (IsHoveringNode && !DidDragNodeHeader && IsHoveringTitle(hoveredNode) && !(e.control || e.shift)) { + if (currentActivity == NodeActivity.HoldHeader && !(e.control || e.shift)) { SelectNode(hoveredNode, false); - Repaint(); } + + Repaint(); + currentActivity = NodeActivity.Idle; } else if (e.button == 1) { if (!isPanning) { if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { - if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode,false); + if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false); ShowNodeContextMenu(); } else if (!IsHoveringNode) { ShowGraphContextMenu(); @@ -158,6 +165,13 @@ namespace XNodeEditor { else if (e.keyCode == KeyCode.D && e.control) DublicateSelectedNodes(); Repaint(); break; + case EventType.Ignore: + // If release mouse outside window + if (e.rawType == EventType.MouseUp && currentActivity == NodeActivity.DragGrid) { + Repaint(); + currentActivity = NodeActivity.Idle; + } + break; } } diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 0153965..32232f0 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -21,6 +21,7 @@ namespace XNodeEditor { DrawConnections(); DrawDraggedConnection(); DrawNodes(); + DrawBox(); DrawTooltip(); GUI.matrix = m; @@ -71,6 +72,17 @@ namespace XNodeEditor { GUI.DrawTextureWithTexCoords(rect, crossTex, new Rect(tileOffset + new Vector2(0.5f, 0.5f), tileAmount)); } + public void DrawBox() { + if (currentActivity == NodeActivity.DragGrid) { + Vector2 curPos = WindowToGridPosition(Event.current.mousePosition); + Vector2 size = curPos - dragBoxStart; + Rect r = new Rect(dragBoxStart, size); + r.position = GridToWindowPosition(r.position); + r.size /= zoom; + Handles.DrawSolidRectangleWithOutline(r, new Color(0, 0, 0, 0.1f), new Color(1, 1, 1, 0.6f)); + } + } + public static bool DropdownButton(string name, float width) { return GUILayout.Button(name, EditorStyles.toolbarDropDown, GUILayout.Width(width)); } @@ -201,6 +213,8 @@ namespace XNodeEditor { hoveredPort = null; } + List preSelection = new List(preBoxSelection); + //Save guiColor so we can revert it Color guiColor = GUI.color; for (int n = 0; n < graph.nodes.Count; n++) { @@ -268,6 +282,19 @@ namespace XNodeEditor { Rect windowRect = new Rect(nodePos, nodeSize); if (windowRect.Contains(mousePos)) hoveredNode = node; + //If dragging a selection box, add nodes inside to selection + if (currentActivity == NodeActivity.DragGrid) { + Vector2 startPos = GridToWindowPosition(dragBoxStart); + Vector2 size = (mousePos - startPos) / zoom; + Rect r = new Rect(dragBoxStart, size); + Vector2 rpos = GridToWindowPosition(r.position); + if (size.x < 0) { rpos.x += size.x; size.x = Mathf.Abs(size.x); } + if (size.y < 0) { rpos.y += size.y; size.y = Mathf.Abs(size.y); } + r.position = rpos; + r.size = size; + if (windowRect.Overlaps(r)) preSelection.Add(node); + } + //Check if we are hovering any of this nodes ports //Check input ports foreach (XNode.NodePort input in node.Inputs) { @@ -288,6 +315,7 @@ namespace XNodeEditor { GUILayout.EndArea(); } + if (e.type != EventType.Layout && currentActivity == NodeActivity.DragGrid) Selection.objects = preSelection.ToArray(); EndZoomed(position, zoom); //If a change in hash is detected in the selected node, call OnValidate method. From 5ae1b16f1477af506e4fc296f3c3a8b2384cdab5 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 25 Jan 2018 00:49:17 +0100 Subject: [PATCH 12/15] Made type color preferences more intuitive --- Scripts/Editor/NodeEditorPreferences.cs | 123 +++++++++++------------- 1 file changed, 57 insertions(+), 66 deletions(-) diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 910d426..59c1285 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -26,8 +26,10 @@ namespace XNodeEditor { /// Have we loaded the prefs yet private static bool prefsLoaded = false; - private static Dictionary typeColors; - private static Dictionary generatedTypeColors; + /// TypeColors requested by the editor + private static Dictionary typeColors = new Dictionary(); + /// TypeColors available in EditorPrefs + private static Dictionary prefsTypeColors = new Dictionary(); public static bool gridSnap { get { VerifyLoaded(); return _gridSnap; } } private static bool _gridSnap = true; public static Color gridLineColor { get { VerifyLoaded(); return _gridLineColor; } } @@ -66,63 +68,24 @@ namespace XNodeEditor { //Label EditorGUILayout.LabelField("Type colors", EditorStyles.boldLabel); - //Get saved type keys - string[] typeKeys = new string[typeColors.Count]; - typeColors.Keys.CopyTo(typeKeys, 0); - //Display saved type colors - foreach (var key in typeKeys) { - EditorGUILayout.BeginHorizontal(); - if (!EditorGUILayout.Toggle(new GUIContent(key, key), true)) { - typeColors.Remove(key); - SavePrefs(); - EditorGUILayout.EndHorizontal(); - continue; - } + //Display type colors. Save them if they are edited by the user + List keys = new List(typeColors.Keys); + foreach (string key in keys) { Color col = typeColors[key]; - col = EditorGUILayout.ColorField(col); - typeColors[key] = col; - EditorGUILayout.EndHorizontal(); - } - if (GUI.changed) { - SavePrefs(); - NodeEditorWindow.RepaintAll(); - } - - //Get generated type keys - string[] generatedTypeKeys = new string[generatedTypeColors.Count]; - generatedTypeColors.Keys.CopyTo(generatedTypeKeys, 0); - //Display generated type colors - foreach (var key in generatedTypeKeys) { + EditorGUI.BeginChangeCheck(); EditorGUILayout.BeginHorizontal(); - if (EditorGUILayout.Toggle(new GUIContent(key, key), false)) { - typeColors.Add(key, generatedTypeColors[key]); - generatedTypeColors.Remove(key); - SavePrefs(); - EditorGUILayout.EndHorizontal(); - continue; - } - Color col = generatedTypeColors[key]; - EditorGUI.BeginDisabledGroup(true); - col = EditorGUILayout.ColorField(col); - EditorGUI.EndDisabledGroup(); - + col = EditorGUILayout.ColorField(key, col); EditorGUILayout.EndHorizontal(); + if (EditorGUI.EndChangeCheck()) { + typeColors[key] = col; + SaveTypeColor(key, col); + NodeEditorWindow.RepaintAll(); + } } } private static void LoadPrefs() { - //Load type colors - generatedTypeColors = new Dictionary(); - - if (!EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.SetString("xnode_typecolors", ""); - string[] data = EditorPrefs.GetString("xnode_typecolors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - typeColors = new Dictionary(); - for (int i = 0; i < data.Length; i += 2) { - Color col; - if (ColorUtility.TryParseHtmlString("#" + data[i + 1], out col)) { - typeColors.Add(data[i], col); - } - } + prefsTypeColors = LoadTypeColors(); //Load grid colors if (!EditorPrefs.HasKey("xnode_gridcolor0")) EditorPrefs.SetString("xnode_gridcolor0", ColorUtility.ToHtmlStringRGB(new Color(0.45f, 0.45f, 0.45f))); @@ -137,6 +100,36 @@ namespace XNodeEditor { prefsLoaded = true; } + /// Get Type Colors from EditorPrefs. Colors are saved as CSV in pairs of two hexcolor/name + public static Dictionary LoadTypeColors() { + //Load type colors + Dictionary result = new Dictionary(); + + if (!EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.SetString("xnode_typecolors", ""); + string[] data = EditorPrefs.GetString("xnode_typecolors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < data.Length; i += 2) { + Color col; + if (ColorUtility.TryParseHtmlString("#" + data[i + 1], out col)) { + result.Add(data[i], col); + } + } + return result; + } + + /// Get Type Colors from EditorPrefs. Colors are saved as CSV in pairs of two hexcolor/name "" + public static Dictionary SaveTypeColor(string typeName, Color col) { + //Load type colors + Dictionary result = LoadTypeColors(); + if (result.ContainsKey(typeName)) result[typeName] = col; + else result.Add(typeName, col); + string s = ""; + foreach (var item in result) { + s += item.Key + "," + ColorUtility.ToHtmlStringRGB(item.Value) + ","; + } + EditorPrefs.SetString("xnode_typecolors", s); + return result; + } + /// Delete all prefs public static void ResetPrefs() { if (EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.DeleteKey("xnode_typecolors"); @@ -146,11 +139,6 @@ namespace XNodeEditor { } private static void SavePrefs() { - string s = ""; - foreach (var item in typeColors) { - s += item.Key + "," + ColorUtility.ToHtmlStringRGB(item.Value) + ","; - } - EditorPrefs.SetString("xnode_typecolors", s); EditorPrefs.SetString("xnode_gridcolor0", ColorUtility.ToHtmlStringRGB(_gridLineColor)); EditorPrefs.SetString("xnode_gridcolor1", ColorUtility.ToHtmlStringRGB(_gridBgColor)); EditorPrefs.SetBool("xnode_gridsnap", _gridSnap); @@ -165,15 +153,18 @@ namespace XNodeEditor { VerifyLoaded(); if (type == null) return Color.gray; string typeName = type.PrettyName(); - if (typeColors.ContainsKey(typeName)) return typeColors[typeName]; - if (generatedTypeColors.ContainsKey(typeName)) return generatedTypeColors[typeName]; - #if UNITY_5_4_OR_NEWER - UnityEngine.Random.InitState(typeName.GetHashCode()); - #else - UnityEngine.Random.seed = typeName.GetHashCode(); - #endif - generatedTypeColors.Add(typeName, new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value)); - return generatedTypeColors[typeName]; + if (!typeColors.ContainsKey(typeName)) { + if (prefsTypeColors.ContainsKey(typeName)) typeColors.Add(typeName, prefsTypeColors[typeName]); + else { +#if UNITY_5_4_OR_NEWER + UnityEngine.Random.InitState(typeName.GetHashCode()); +#else + UnityEngine.Random.seed = typeName.GetHashCode(); +#endif + typeColors.Add(typeName, new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value)); + } + } + return typeColors[typeName]; } } } \ No newline at end of file From 9dc5653f27dabd6e533df6d70f6fdd620be13c41 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 25 Jan 2018 10:27:55 +0100 Subject: [PATCH 13/15] Simplified editor preferences handling. This update will reset your xnode editor preferences --- Scripts/Editor/NodeEditorPreferences.cs | 133 +++++++++++------------- 1 file changed, 61 insertions(+), 72 deletions(-) diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 59c1285..a34d678 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -9,7 +9,7 @@ namespace XNodeEditor { public static Texture2D gridTexture { get { VerifyLoaded(); - if (_gridTexture == null) _gridTexture = NodeEditorResources.GenerateGridTexture(_gridLineColor, _gridBgColor); + if (_gridTexture == null) _gridTexture = NodeEditorResources.GenerateGridTexture(settings.gridLineColor, settings.gridBgColor); return _gridTexture; } } @@ -17,25 +17,47 @@ namespace XNodeEditor { public static Texture2D crossTexture { get { VerifyLoaded(); - if (_crossTexture == null) _crossTexture = NodeEditorResources.GenerateCrossTexture(_gridLineColor); + if (_crossTexture == null) _crossTexture = NodeEditorResources.GenerateCrossTexture(settings.gridLineColor); return _crossTexture; } } private static Texture2D _crossTexture; - /// Have we loaded the prefs yet - private static bool prefsLoaded = false; - /// TypeColors requested by the editor + public static bool gridSnap { get { VerifyLoaded(); return settings.gridSnap; } } + private static Dictionary typeColors = new Dictionary(); - /// TypeColors available in EditorPrefs - private static Dictionary prefsTypeColors = new Dictionary(); - public static bool gridSnap { get { VerifyLoaded(); return _gridSnap; } } - private static bool _gridSnap = true; - public static Color gridLineColor { get { VerifyLoaded(); return _gridLineColor; } } - private static Color _gridLineColor; - public static Color gridBgColor { get { VerifyLoaded(); return _gridBgColor; } } - private static Color _gridBgColor; + private static Settings settings; + + [System.Serializable] + private class Settings : ISerializationCallbackReceiver { + public Color32 gridLineColor = new Color(0.45f, 0.45f, 0.45f); + public Color32 gridBgColor = new Color(0.18f, 0.18f, 0.18f); + public bool gridSnap = true; + public string typeColorsData = ""; + public Dictionary typeColors = new Dictionary(); + + public void OnAfterDeserialize() { + // Deserialize typeColorsData + typeColors = new Dictionary(); + string[] data = typeColorsData.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < data.Length; i += 2) { + Color col; + if (ColorUtility.TryParseHtmlString("#" + data[i + 1], out col)) { + typeColors.Add(data[i], col); + } + } + } + + public void OnBeforeSerialize() { + // Serialize typeColors + typeColorsData = ""; + foreach (var item in typeColors) { + typeColorsData += item.Key + "," + ColorUtility.ToHtmlStringRGB(item.Value) + ","; + } + } + } + [PreferenceItem("Node Editor")] private static void PreferencesGUI() { VerifyLoaded(); @@ -50,15 +72,14 @@ namespace XNodeEditor { private static void GridSettingsGUI() { //Label EditorGUILayout.LabelField("Grid", EditorStyles.boldLabel); - _gridSnap = EditorGUILayout.Toggle("Snap", _gridSnap); + settings.gridSnap = EditorGUILayout.Toggle("Snap", settings.gridSnap); - //EditorGUIUtility.labelWidth = 30; - _gridLineColor = EditorGUILayout.ColorField("Color", _gridLineColor); - _gridBgColor = EditorGUILayout.ColorField(" ", _gridBgColor); + settings.gridLineColor = EditorGUILayout.ColorField("Color", settings.gridLineColor); + settings.gridBgColor = EditorGUILayout.ColorField(" ", settings.gridBgColor); if (GUI.changed) { SavePrefs(); - _gridTexture = NodeEditorResources.GenerateGridTexture(_gridLineColor, _gridBgColor); - _crossTexture = NodeEditorResources.GenerateCrossTexture(_gridLineColor); + _gridTexture = NodeEditorResources.GenerateGridTexture(settings.gridLineColor, settings.gridBgColor); + _crossTexture = NodeEditorResources.GenerateCrossTexture(settings.gridLineColor); NodeEditorWindow.RepaintAll(); } EditorGUILayout.Space(); @@ -78,74 +99,42 @@ namespace XNodeEditor { EditorGUILayout.EndHorizontal(); if (EditorGUI.EndChangeCheck()) { typeColors[key] = col; - SaveTypeColor(key, col); + if (settings.typeColors.ContainsKey(key)) settings.typeColors[key] = col; + else settings.typeColors.Add(key, col); + SavePrefs(); NodeEditorWindow.RepaintAll(); } } } - private static void LoadPrefs() { - prefsTypeColors = LoadTypeColors(); + private static Settings LoadPrefs() { + // Remove obsolete editorprefs + if (EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.DeleteKey("xnode_typecolors"); + if (EditorPrefs.HasKey("xnode_gridcolor0")) EditorPrefs.DeleteKey("xnode_gridcolor0"); + if (EditorPrefs.HasKey("xnode_gridcolor1")) EditorPrefs.DeleteKey("xnode_gridcolor1"); + if (EditorPrefs.HasKey("xnode_gridsnap")) EditorPrefs.DeleteKey("xnode_gridcolor1"); - //Load grid colors - if (!EditorPrefs.HasKey("xnode_gridcolor0")) EditorPrefs.SetString("xnode_gridcolor0", ColorUtility.ToHtmlStringRGB(new Color(0.45f, 0.45f, 0.45f))); - ColorUtility.TryParseHtmlString("#" + EditorPrefs.GetString("xnode_gridcolor0"), out _gridLineColor); - if (!EditorPrefs.HasKey("xnode_gridcolor1")) EditorPrefs.SetString("xnode_gridcolor1", ColorUtility.ToHtmlStringRGB(new Color(0.18f, 0.18f, 0.18f))); - ColorUtility.TryParseHtmlString("#" + EditorPrefs.GetString("xnode_gridcolor1"), out _gridBgColor); - - //Load snap option - if (EditorPrefs.HasKey("xnode_gridsnap")) _gridSnap = EditorPrefs.GetBool("xnode_gridsnap"); - - NodeEditorWindow.RepaintAll(); - prefsLoaded = true; - } - - /// Get Type Colors from EditorPrefs. Colors are saved as CSV in pairs of two hexcolor/name - public static Dictionary LoadTypeColors() { - //Load type colors - Dictionary result = new Dictionary(); - - if (!EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.SetString("xnode_typecolors", ""); - string[] data = EditorPrefs.GetString("xnode_typecolors").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < data.Length; i += 2) { - Color col; - if (ColorUtility.TryParseHtmlString("#" + data[i + 1], out col)) { - result.Add(data[i], col); - } - } - return result; - } - - /// Get Type Colors from EditorPrefs. Colors are saved as CSV in pairs of two hexcolor/name "" - public static Dictionary SaveTypeColor(string typeName, Color col) { - //Load type colors - Dictionary result = LoadTypeColors(); - if (result.ContainsKey(typeName)) result[typeName] = col; - else result.Add(typeName, col); - string s = ""; - foreach (var item in result) { - s += item.Key + "," + ColorUtility.ToHtmlStringRGB(item.Value) + ","; - } - EditorPrefs.SetString("xnode_typecolors", s); - return result; + if (!EditorPrefs.HasKey("xNode.Settings")) EditorPrefs.SetString("xNode.Settings", JsonUtility.ToJson(new Settings())); + return JsonUtility.FromJson(EditorPrefs.GetString("xNode.Settings")); } /// Delete all prefs public static void ResetPrefs() { - if (EditorPrefs.HasKey("xnode_typecolors")) EditorPrefs.DeleteKey("xnode_typecolors"); - if (EditorPrefs.HasKey("xnode_gridcolor0")) EditorPrefs.DeleteKey("xnode_gridcolor0"); - if (EditorPrefs.HasKey("xnode_gridcolor1")) EditorPrefs.DeleteKey("xnode_gridcolor1"); - LoadPrefs(); + if (EditorPrefs.HasKey("xNode.Settings")) EditorPrefs.DeleteKey("xNode.Settings"); + + settings = LoadPrefs(); + typeColors = new Dictionary(); + _gridTexture = NodeEditorResources.GenerateGridTexture(settings.gridLineColor, settings.gridBgColor); + _crossTexture = NodeEditorResources.GenerateCrossTexture(settings.gridLineColor); + NodeEditorWindow.RepaintAll(); } private static void SavePrefs() { - EditorPrefs.SetString("xnode_gridcolor0", ColorUtility.ToHtmlStringRGB(_gridLineColor)); - EditorPrefs.SetString("xnode_gridcolor1", ColorUtility.ToHtmlStringRGB(_gridBgColor)); - EditorPrefs.SetBool("xnode_gridsnap", _gridSnap); + EditorPrefs.SetString("xNode.Settings", JsonUtility.ToJson(settings)); } private static void VerifyLoaded() { - if (!prefsLoaded) LoadPrefs(); + if (settings == null) settings = LoadPrefs(); } /// Return color based on type @@ -154,7 +143,7 @@ namespace XNodeEditor { if (type == null) return Color.gray; string typeName = type.PrettyName(); if (!typeColors.ContainsKey(typeName)) { - if (prefsTypeColors.ContainsKey(typeName)) typeColors.Add(typeName, prefsTypeColors[typeName]); + if (settings.typeColors.ContainsKey(typeName)) typeColors.Add(typeName, settings.typeColors[typeName]); else { #if UNITY_5_4_OR_NEWER UnityEngine.Random.InitState(typeName.GetHashCode()); From 14c4d0b691ee3ef6e4b16564c88d3cd4731acaaf Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 25 Jan 2018 10:43:03 +0100 Subject: [PATCH 14/15] Added color settings for selection outlines --- Scripts/Editor/NodeEditorAction.cs | 2 +- Scripts/Editor/NodeEditorGUI.cs | 2 +- Scripts/Editor/NodeEditorPreferences.cs | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 6e5e035..41bb196 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -46,7 +46,7 @@ namespace XNodeEditor { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; node.position = WindowToGridPosition(e.mousePosition) + dragOffset[i]; - if (NodeEditorPreferences.gridSnap) { + if (NodeEditorPreferences.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; } diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 32232f0..fc28784 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -240,7 +240,7 @@ namespace XNodeEditor { style.padding = new RectOffset(); GUI.color = nodeEditor.GetTint(); GUILayout.BeginVertical(new GUIStyle(style)); - GUI.color = Color.white; + GUI.color = NodeEditorPreferences.HighlightColor; GUILayout.BeginVertical(new GUIStyle(highlightStyle)); } else { GUIStyle style = NodeEditorResources.styles.nodeBody; diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index a34d678..0df1ad2 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -23,8 +23,8 @@ namespace XNodeEditor { } private static Texture2D _crossTexture; - /// TypeColors requested by the editor - public static bool gridSnap { get { VerifyLoaded(); return settings.gridSnap; } } + public static bool GridSnap { get { VerifyLoaded(); return settings.gridSnap; } } + public static Color HighlightColor { get { VerifyLoaded(); return settings.highlightColor; } } private static Dictionary typeColors = new Dictionary(); private static Settings settings; @@ -33,6 +33,7 @@ namespace XNodeEditor { private class Settings : ISerializationCallbackReceiver { public Color32 gridLineColor = new Color(0.45f, 0.45f, 0.45f); public Color32 gridBgColor = new Color(0.18f, 0.18f, 0.18f); + public Color32 highlightColor = new Color32(255, 223, 255, 255); public bool gridSnap = true; public string typeColorsData = ""; public Dictionary typeColors = new Dictionary(); @@ -62,6 +63,7 @@ namespace XNodeEditor { private static void PreferencesGUI() { VerifyLoaded(); + NodeSettingsGUI(); GridSettingsGUI(); TypeColorsGUI(); if (GUILayout.Button(new GUIContent("Set Default", "Reset all values to default"), GUILayout.Width(120))) { @@ -85,9 +87,20 @@ namespace XNodeEditor { EditorGUILayout.Space(); } + private static void NodeSettingsGUI() { + //Label + EditorGUILayout.LabelField("Node", EditorStyles.boldLabel); + settings.highlightColor = EditorGUILayout.ColorField("Selection", settings.highlightColor); + if (GUI.changed) { + SavePrefs(); + NodeEditorWindow.RepaintAll(); + } + EditorGUILayout.Space(); + } + private static void TypeColorsGUI() { //Label - EditorGUILayout.LabelField("Type colors", EditorStyles.boldLabel); + EditorGUILayout.LabelField("Types", EditorStyles.boldLabel); //Display type colors. Save them if they are edited by the user List keys = new List(typeColors.Keys); From 6758a1a7c4a2f01cbaa1caebaca74aee36585834 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 25 Jan 2018 12:42:35 +0100 Subject: [PATCH 15/15] Fixed box selection not working correctly when zoomed --- Scripts/Editor/NodeEditorGUI.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index fc28784..e3f8c4c 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -284,14 +284,11 @@ namespace XNodeEditor { //If dragging a selection box, add nodes inside to selection if (currentActivity == NodeActivity.DragGrid) { - Vector2 startPos = GridToWindowPosition(dragBoxStart); - Vector2 size = (mousePos - startPos) / zoom; - Rect r = new Rect(dragBoxStart, size); - Vector2 rpos = GridToWindowPosition(r.position); - if (size.x < 0) { rpos.x += size.x; size.x = Mathf.Abs(size.x); } - if (size.y < 0) { rpos.y += size.y; size.y = Mathf.Abs(size.y); } - r.position = rpos; - r.size = size; + Vector2 startPos = GridToWindowPositionNoClipped(dragBoxStart); + Vector2 size = mousePos - startPos; + if (size.x < 0) { startPos.x += size.x; size.x = Mathf.Abs(size.x); } + if (size.y < 0) { startPos.y += size.y; size.y = Mathf.Abs(size.y); } + Rect r = new Rect(startPos, size); if (windowRect.Overlaps(r)) preSelection.Add(node); }