From c66ac47ea9259d62f565c081d19a7d0e4659ed39 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 23 Sep 2017 00:26:38 +0200 Subject: [PATCH] Changed NodePort UI styling --- Scripts/Editor/NodeEditor.cs | 66 ++++++++++++- Scripts/Editor/NodeEditorAction.cs | 19 ++-- Scripts/Editor/NodeEditorGUI.cs | 58 +++++------- Scripts/Editor/NodeEditorResources.cs | 64 ++++--------- Scripts/Editor/NodeEditorUtilities.cs | 20 ++++ Scripts/Editor/NodeEditorWindow.cs | 5 +- Scripts/Editor/Resources.meta | 9 ++ Scripts/Editor/Resources/dot.png | Bin 0 -> 18297 bytes Scripts/Editor/Resources/dot.png.meta | 98 ++++++++++++++++++++ Scripts/Editor/Resources/dot_outer.png | Bin 0 -> 18346 bytes Scripts/Editor/Resources/dot_outer.png.meta | 98 ++++++++++++++++++++ 11 files changed, 342 insertions(+), 95 deletions(-) create mode 100644 Scripts/Editor/Resources.meta create mode 100644 Scripts/Editor/Resources/dot.png create mode 100644 Scripts/Editor/Resources/dot.png.meta create mode 100644 Scripts/Editor/Resources/dot_outer.png create mode 100644 Scripts/Editor/Resources/dot_outer.png.meta diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index f763f3b..c9bf764 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.Reflection; @@ -9,9 +8,12 @@ using System; /// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. public class NodeEditor { + public Dictionary portRects = new Dictionary(); public Node target; public virtual void OnNodeGUI() { + portRects.Clear(); + DrawNodePortsGUI(); DrawDefaultNodeGUI(); } @@ -85,6 +87,66 @@ public class NodeEditor { EditorGUILayout.Space(); } + protected void DrawNodePortsGUI() { + + Event e = Event.current; + + GUILayout.BeginHorizontal(); + + //Inputs + GUILayout.BeginVertical(); + for (int i = 0; i < target.InputCount; i++) { + DrawNodePortGUI(target.GetInput(i)); + //NodePort input = target.GetInput(i); + //Rect r = GUILayoutUtility.GetRect(new GUIContent(input.name), NodeEditorResources.styles.GetInputStyle(input.type)); + //GUI.Label(r, input.name, NodeEditorResources.styles.GetInputStyle(input.type)); + //if (e.type == EventType.Repaint) portRects.Add(input, r); + //portConnectionPoints.Add(input, new Vector2(r.xMin, r.yMin + (r.height * 0.5f)) + target.position.position); + } + GUILayout.EndVertical(); + + //Outputs + GUILayout.BeginVertical(); + for (int i = 0; i < target.OutputCount; i++) { + DrawNodePortGUI(target.GetOutput(i)); + //NodePort output = target.GetOutput(i); + //Rect r = GUILayoutUtility.GetRect(new GUIContent(output.name), NodeEditorResources.styles.GetOutputStyle(output.type)); + //GUI.Label(r, output.name, NodeEditorResources.styles.GetOutputStyle(output.type)); + //if (e.type == EventType.Repaint) portRects.Add(output, r); + //portConnectionPoints.Add(output, new Vector2(r.xMax, r.yMin + (r.height * 0.5f)) + target.position.position); + } + GUILayout.EndVertical(); + + GUILayout.EndHorizontal(); + } + + protected void DrawNodePortGUI(NodePort port) { + GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle; + Rect rect = GUILayoutUtility.GetRect(new GUIContent(port.name), style); + DrawNodePortGUI(rect, port); + } + + protected void DrawNodePortGUI(Rect rect, NodePort port) { + GUIStyle style = port.direction == NodePort.IO.Input ? NodeEditorResources.styles.inputStyle : NodeEditorResources.styles.outputStyle; + GUI.Label(rect, new GUIContent(port.name), style); + Rect handleRect = new Rect(0, 0, 16, 16); + switch (port.direction) { + case NodePort.IO.Input: + handleRect.position = new Vector2(rect.xMin - 8, rect.position.y + (rect.height * 0.5f) - 8); + break; + case NodePort.IO.Output: + handleRect.position = new Vector2(rect.xMax - 8, rect.position.y + (rect.height * 0.5f) - 8); + break; + } + portRects.Add(port, handleRect); + Color col = GUI.color; + GUI.color = NodeEditorUtilities.GetTypeColor(port.type); + GUI.DrawTexture(handleRect, NodeEditorResources.dot); + GUI.color = Color.black; + GUI.DrawTexture(handleRect, NodeEditorResources.dotOuter); + GUI.color = col; + } + private static FieldInfo[] GetInspectorFields(Node node) { return node.GetType().GetFields().Where(f => f.IsPublic || f.GetCustomAttributes(typeof(SerializeField),false) != null).ToArray(); } diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 4cef3e4..e27e518 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -122,18 +122,20 @@ public partial class NodeEditorWindow { public void CreateNode(Type type, Vector2 position) { Node node = graph.AddNode(type); node.position.position = position; + Repaint(); } /// Draw a connection as we are dragging it public void DrawDraggedConnection() { if (IsDraggingPort) { if (!_portConnectionPoints.ContainsKey(draggedOutput)) return; - Vector2 from = _portConnectionPoints[draggedOutput]; - Vector2 to = draggedOutputTarget != null ? portConnectionPoints[draggedOutputTarget] : WindowToGridPosition(Event.current.mousePosition); + Vector2 from = draggedOutput.node.position.position + _portConnectionPoints[draggedOutput].center; + Vector2 to = draggedOutputTarget != null ? draggedOutputTarget.node.position.position + portConnectionPoints[draggedOutputTarget].center : WindowToGridPosition(Event.current.mousePosition); DrawConnection(from, to); } } + /// Updates and void UpdateHovered() { Vector2 mousePos = Event.current.mousePosition; Node newHoverNode = null; @@ -149,20 +151,25 @@ public partial class NodeEditorWindow { hoveredNode = newHoverNode; Repaint(); } + //If we are hovering a node, check if we are also hovering a port if (IsHoveringNode) { NodePort newHoverPort = null; + //Check all input ports for (int i = 0; i < hoveredNode.InputCount; i++) { NodePort port = hoveredNode.GetInput(i); - if (!portRects.ContainsKey(port)) continue; - Rect r = portRects[port]; + //Check if port rect is available + if (!portConnectionPoints.ContainsKey(port)) continue; + Rect r = portConnectionPoints[port]; r.position = GridToWindowPosition(r.position + hoveredNode.position.position); r.size /= zoom; if (r.Contains(mousePos)) newHoverPort = port; } + //Check all output ports for (int i = 0; i < hoveredNode.OutputCount; i++) { NodePort port = hoveredNode.GetOutput(i); - if (!portRects.ContainsKey(port)) continue; - Rect r = portRects[port]; + //Check if port rect is available + if (!portConnectionPoints.ContainsKey(port)) continue; + Rect r = portConnectionPoints[port]; r.position = GridToWindowPosition(r.position + hoveredNode.position.position); r.size /= zoom; if (r.Contains(mousePos)) newHoverPort = port; diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 495ad00..f8316f6 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -1,6 +1,4 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; +using UnityEngine; using UnityEditor; using System; @@ -52,8 +50,8 @@ public partial class NodeEditorWindow { rect.position = Vector2.zero; Vector2 center = rect.size / 2f; - Texture2D gridTex = gridTexture; - Texture2D crossTex = crossTexture; + Texture2D gridTex = NodeEditorResources.gridTexture; + Texture2D crossTex = NodeEditorResources.crossTexture; // Offset from origin in tile units float xOffset = -(center.x * zoom + panOffset.x) / gridTex.width; @@ -103,7 +101,7 @@ public partial class NodeEditorWindow { contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); } - public void DrawConnection(Vector2 startPoint, Vector2 endPoint) { + public void DrawConnection(Vector2 startPoint, Vector2 endPoint) { startPoint = GridToWindowPosition(startPoint); endPoint = GridToWindowPosition(endPoint); @@ -123,10 +121,13 @@ public partial class NodeEditorWindow { foreach (Node node in graph.nodes) { for (int i = 0; i < node.OutputCount; i++) { NodePort output = node.GetOutput(i); - Vector2 from = _portConnectionPoints[output]; + + //Needs cleanup. Null checks are ugly + if (!portConnectionPoints.ContainsKey(output)) continue; + Vector2 from = _portConnectionPoints[output].center + node.position.position; for (int k = 0; k < output.ConnectionCount; k++) { NodePort input = output.GetConnection(k); - Vector2 to = _portConnectionPoints[input]; + Vector2 to = input.node.position.position + _portConnectionPoints[input].center; DrawConnection(from, to); } } @@ -134,46 +135,23 @@ public partial class NodeEditorWindow { } private void DrawNodes() { - portConnectionPoints.Clear(); Event e = Event.current; + if (e.type == EventType.Repaint) portConnectionPoints.Clear(); BeginWindows(); BeginZoomed(position, zoom); - if (e.type == EventType.Repaint) portRects.Clear(); + //if (e.type == EventType.Repaint) portRects.Clear(); foreach (Node node in graph.nodes) { //Get node position Vector2 nodePos = GridToWindowPositionNoClipped(node.position.position); GUIStyle style = (node == selectedNode) ? (GUIStyle)"flow node 0 on" : (GUIStyle)"flow node 0"; - GUILayout.BeginArea(new Rect(nodePos,new Vector2(200,4000))); + GUILayout.BeginArea(new Rect(nodePos,new Vector2(240,4000))); string nodeName = string.IsNullOrEmpty(node.name) ? node.ToString() : node.name; - GUILayout.BeginVertical(nodeName, style); GUILayout.BeginHorizontal(); - - //Inputs - GUILayout.BeginVertical(); - for (int i = 0; i < node.InputCount; i++) { - NodePort input = node.GetInput(i); - Rect r = GUILayoutUtility.GetRect(new GUIContent(input.name), styles.GetInputStyle(input.type)); - GUI.Label(r, input.name, styles.GetInputStyle(input.type)); - if (e.type == EventType.Repaint) portRects.Add(input, r); - portConnectionPoints.Add(input, new Vector2(r.xMin, r.yMin + (r.height * 0.5f)) + node.position.position); - } - GUILayout.EndVertical(); - - //Outputs - GUILayout.BeginVertical(); - for (int i = 0; i < node.OutputCount; i++) { - NodePort output = node.GetOutput(i); - Rect r = GUILayoutUtility.GetRect(new GUIContent(output.name), styles.GetOutputStyle(output.type)); - GUI.Label(r, output.name, styles.GetOutputStyle(output.type)); - if (e.type == EventType.Repaint) portRects.Add(output, r); - portConnectionPoints.Add(output, new Vector2(r.xMax, r.yMin + (r.height * 0.5f)) + node.position.position); - } - GUILayout.EndVertical(); - - GUILayout.EndHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.BeginVertical(nodeName, style, GUILayout.Width(200)); NodeEditor nodeEditor = GetNodeEditor(node.GetType()); @@ -185,12 +163,20 @@ public partial class NodeEditorWindow { if (onValidate != null) nodeHash = node.GetHashCode(); nodeEditor.OnNodeGUI(); + if (e.type == EventType.Repaint) { + foreach (var kvp in nodeEditor.portRects) { + portConnectionPoints.Add(kvp.Key, kvp.Value); + } + } //If a change in hash is detected, 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 (onValidate != null && nodeHash != node.GetHashCode()) onValidate.Invoke(node, null); GUILayout.EndVertical(); + GUILayout.FlexibleSpace(); + + GUILayout.EndHorizontal(); if (e.type == EventType.Repaint) node.position.size = GUILayoutUtility.GetLastRect().size; GUILayout.EndArea(); } diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs index 44e23d8..5688bf7 100644 --- a/Scripts/Editor/NodeEditorResources.cs +++ b/Scripts/Editor/NodeEditorResources.cs @@ -2,13 +2,16 @@ using UnityEditor; using System; -public partial class NodeEditorWindow { +public static class NodeEditorResources { public static Texture2D gridTexture { get { return _gridTexture != null ? _gridTexture : _gridTexture = GenerateGridTexture(); } } private static Texture2D _gridTexture; public static Texture2D crossTexture { get { return _crossTexture != null ? _crossTexture : _crossTexture = GenerateCrossTexture(); } } private static Texture2D _crossTexture; - + public static Texture2D dot { get { return _dot != null ? _dot : _dot = Resources.Load("dot"); } } + private static Texture2D _dot; + public static Texture2D dotOuter { get { return _dotOuter != null ? _dotOuter : _dotOuter = Resources.Load("dot_outer"); } } + private static Texture2D _dotOuter; private static Color backgroundColor = new Color(0.18f, 0.18f, 0.18f); private static Color veinColor = new Color(0.25f, 0.25f, 0.25f); @@ -19,56 +22,21 @@ public partial class NodeEditorWindow { public static Styles _styles = null; public class Styles { - GUIStyle inputInt, inputString, inputFloat, inputObject, inputTexture, inputColor; - GUIStyle outputInt, outputString, outputFloat, outputObject, outputTexture, outputColor; + public GUIStyle inputStyle, outputStyle; public Styles() { - - inputObject = new GUIStyle((GUIStyle)"flow shader in 0"); - inputString = new GUIStyle((GUIStyle)"flow shader in 1"); - inputInt = new GUIStyle((GUIStyle)"flow shader in 2"); - inputFloat = new GUIStyle((GUIStyle)"flow shader in 3"); - inputColor = new GUIStyle((GUIStyle)"flow shader in 4"); - inputTexture = new GUIStyle((GUIStyle)"flow shader in 5"); - outputObject = new GUIStyle((GUIStyle)"flow shader out 0"); - outputString = new GUIStyle((GUIStyle)"flow shader out 1"); - outputInt = new GUIStyle((GUIStyle)"flow shader out 2"); - outputFloat = new GUIStyle((GUIStyle)"flow shader out 3"); - outputColor = new GUIStyle((GUIStyle)"flow shader out 4"); - outputTexture = new GUIStyle((GUIStyle)"flow shader out 5"); + GUIStyle baseStyle = new GUIStyle(); + baseStyle.normal.textColor = Color.black; + baseStyle.onHover.textColor = Color.red; + baseStyle.fixedHeight = 18; - foreach (GUIStyle style in new GUIStyle[] { inputInt, inputString, inputFloat, inputObject, inputTexture, inputColor }) { - style.normal.textColor = Color.black; - style.fixedHeight = 18; - style.alignment = TextAnchor.MiddleLeft; - style.onHover.textColor = Color.red; - } + inputStyle = new GUIStyle(baseStyle); + inputStyle.alignment = TextAnchor.UpperLeft; + inputStyle.padding.left = 10; - foreach (GUIStyle style in new GUIStyle[] { outputInt, outputString, outputFloat, outputObject, outputTexture, outputColor }) { - style.normal.textColor = Color.black; - style.fixedHeight = 18; - style.alignment = TextAnchor.MiddleRight; - style.onHover.textColor = Color.red; - } - } - - public GUIStyle GetInputStyle(Type type) { - - if (type == typeof(int)) return inputInt; - else if (type == typeof(string)) return inputString; - else if (type == typeof(Texture2D)) return inputTexture; - else if (type == typeof(float)) return inputFloat; - else if (type == typeof(Color)) return inputColor; - else return inputObject; - } - - public GUIStyle GetOutputStyle(Type type) { - if (type == typeof(int)) return outputInt; - else if (type == typeof(string)) return outputString; - else if (type == typeof(Texture2D)) return outputTexture; - else if (type == typeof(float)) return outputFloat; - else if (type == typeof(Color)) return outputColor; - else return outputObject; + outputStyle = new GUIStyle(baseStyle); + outputStyle.alignment = TextAnchor.UpperRight; + outputStyle.padding.right = 10; } } diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index 37af764..81c3e1c 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -1,6 +1,8 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; +using System.Reflection; +using System.Linq; using System; public static class NodeEditorUtilities { @@ -20,4 +22,22 @@ public static class NodeEditorUtilities { attribOut = null; return false; } + + /// Return color based on type + public static Color GetTypeColor(Type type) { + UnityEngine.Random.InitState(type.Name.GetHashCode()); + return new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value); + } + + /// Returns true if this can be casted to + public static bool IsCastableTo(this Type from, Type to) { + if (to.IsAssignableFrom(from)) return true; + var methods = from.GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where( + m => m.ReturnType == to && + (m.Name == "op_Implicit" || + m.Name == "op_Explicit") + ); + return methods.Count() > 0; + } } diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index 7928030..ce1c760 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -8,9 +8,8 @@ using System; [InitializeOnLoad] public partial class NodeEditorWindow : EditorWindow { - public Dictionary portConnectionPoints { get { return _portConnectionPoints; } } - private Dictionary _portConnectionPoints = new Dictionary(); - private Dictionary portRects = new Dictionary(); + public Dictionary portConnectionPoints { get { return _portConnectionPoints; } } + private Dictionary _portConnectionPoints = new Dictionary(); public NodeGraph graph { get { return _graph != null ? _graph : _graph = CreateInstance(); } } public NodeGraph _graph; public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } } diff --git a/Scripts/Editor/Resources.meta b/Scripts/Editor/Resources.meta new file mode 100644 index 0000000..786ef41 --- /dev/null +++ b/Scripts/Editor/Resources.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 964fc201163fe884ca6a20094b6f3b49 +folderAsset: yes +timeCreated: 1506110871 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Resources/dot.png b/Scripts/Editor/Resources/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..36c0ce937490b4380137cd287a00095f16c59b31 GIT binary patch literal 18297 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 B2}l3{ literal 0 HcmV?d00001 diff --git a/Scripts/Editor/Resources/dot.png.meta b/Scripts/Editor/Resources/dot.png.meta new file mode 100644 index 0000000..00c23bc --- /dev/null +++ b/Scripts/Editor/Resources/dot.png.meta @@ -0,0 +1,98 @@ +fileFormatVersion: 2 +guid: 75a1fe0b102226a418486ed823c9a7fb +timeCreated: 1506110357 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + 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 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: WebGL + maxTextureSize: 2048 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Resources/dot_outer.png b/Scripts/Editor/Resources/dot_outer.png new file mode 100644 index 0000000000000000000000000000000000000000..538cc9fa1ed169cf7797e8cf4304b202f5754b8e GIT binary patch literal 18346 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