1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-20 09:16:01 +08:00

Changed NodePort UI styling

This commit is contained in:
Unknown 2017-09-23 00:26:38 +02:00
parent f00b957e04
commit c66ac47ea9
11 changed files with 342 additions and 95 deletions

View File

@ -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;
/// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary>
public class NodeEditor {
public Dictionary<NodePort, Rect> portRects = new Dictionary<NodePort, Rect>();
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();
}

View File

@ -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();
}
/// <summary> Draw a connection as we are dragging it </summary>
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);
}
}
/// <summary> Updates <see cref="hoveredNode"/> and <see cref="hoveredPort"/> </summary>
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;

View File

@ -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();
}

View File

@ -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<Texture2D>("dot"); } }
private static Texture2D _dot;
public static Texture2D dotOuter { get { return _dotOuter != null ? _dotOuter : _dotOuter = Resources.Load<Texture2D>("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() {
GUIStyle baseStyle = new GUIStyle();
baseStyle.normal.textColor = Color.black;
baseStyle.onHover.textColor = Color.red;
baseStyle.fixedHeight = 18;
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");
inputStyle = new GUIStyle(baseStyle);
inputStyle.alignment = TextAnchor.UpperLeft;
inputStyle.padding.left = 10;
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;
}
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;
}
}

View File

@ -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;
}
/// <summary> Return color based on type </summary>
public static Color GetTypeColor(Type type) {
UnityEngine.Random.InitState(type.Name.GetHashCode());
return new Color(UnityEngine.Random.value, UnityEngine.Random.value, UnityEngine.Random.value);
}
/// <summary> Returns true if this can be casted to <see cref="Type"/></summary>
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;
}
}

View File

@ -8,9 +8,8 @@ using System;
[InitializeOnLoad]
public partial class NodeEditorWindow : EditorWindow {
public Dictionary<NodePort, Vector2> portConnectionPoints { get { return _portConnectionPoints; } }
private Dictionary<NodePort, Vector2> _portConnectionPoints = new Dictionary<NodePort, Vector2>();
private Dictionary<NodePort, Rect> portRects = new Dictionary<NodePort, Rect>();
public Dictionary<NodePort, Rect> portConnectionPoints { get { return _portConnectionPoints; } }
private Dictionary<NodePort, Rect> _portConnectionPoints = new Dictionary<NodePort, Rect>();
public NodeGraph graph { get { return _graph != null ? _graph : _graph = CreateInstance<NodeGraph>(); } }
public NodeGraph _graph;
public Vector2 panOffset { get { return _panOffset; } set { _panOffset = value; Repaint(); } }

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 964fc201163fe884ca6a20094b6f3b49
folderAsset: yes
timeCreated: 1506110871
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -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:

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,98 @@
fileFormatVersion: 2
guid: 434ca8b4bdfa5574abb0002bbc9b65ad
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: