1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-21 01:36:03 +08:00

OnBodyGUI() remake

Default OnBodyGUI now uses default PropertyFields as suppied by UnityEditor.
This means fields are drawn on nodes the same way as they are drawn in the inspector.
out portPosition has also been moved to a static field. It is set automatically using the NodeEditorGuiLayout methods.
Alternatively, you can alter it manually during your node GUI drawing.
This commit is contained in:
Thor Kramer Brigsted 2017-10-31 14:52:10 +01:00
parent f335ecfbb5
commit ab74f8fd36
4 changed files with 68 additions and 242 deletions

View File

@ -7,8 +7,8 @@ namespace BasicNodes {
[CustomNodeEditor(typeof(DisplayValue), "BasicNodes/DisplayValue")]
public class DisplayValueEditor : NodeEditor {
protected override void OnBodyGUI(out Dictionary<NodePort, Vector2> portPositions) {
base.OnBodyGUI(out portPositions);
protected override void OnBodyGUI() {
base.OnBodyGUI();
object obj = target.GetValue(null);
if (obj != null) EditorGUILayout.LabelField(target.GetValue(null).ToString());
}

View File

@ -10,38 +10,35 @@ public class NodeEditor {
/// <summary> Fires every whenever a node was modified through the editor </summary>
public static Action<Node> onUpdateNode;
public Node target;
public SerializedObject serializedObject;
public static Dictionary<NodePort, Vector2> portPositions;
/// <summary> Draws the node GUI.</summary>
/// <param name="portPositions">Port handle positions need to be returned to the NodeEditorWindow </param>
public void OnNodeGUI(out Dictionary<NodePort, Vector2> portPositions) {
public void OnNodeGUI() {
OnHeaderGUI();
OnBodyGUI(out portPositions);
OnBodyGUI();
}
protected virtual void OnHeaderGUI() {
protected void OnHeaderGUI() {
GUI.color = Color.white;
string title = NodeEditorUtilities.PrettifyCamelCase(target.name);
GUILayout.Label(title, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
}
/// <summary> Draws standard field editors for all public fields </summary>
protected virtual void OnBodyGUI(out Dictionary<NodePort, Vector2> portPositions) {
protected virtual void OnBodyGUI() {
string[] excludes = { "m_Script", "graph", "position", "inputs", "outputs" };
portPositions = new Dictionary<NodePort, Vector2>();
EditorGUI.BeginChangeCheck();
FieldInfo[] fields = GetInspectorFields(target);
for (int i = 0; i < fields.Length; i++) {
switch (fields[i].Name) { case "graph": case "position": case "inputs": case "outputs": continue; }
NodeEditorGUILayout.PropertyField(target, fields[i], portPositions);
SerializedProperty iterator = serializedObject.GetIterator();
bool enterChildren = true;
EditorGUIUtility.labelWidth = 84;
while (iterator.NextVisible(enterChildren)) {
enterChildren = false;
if (excludes.Contains(iterator.name)) continue;
NodeEditorGUILayout.PropertyField(iterator, true);
}
//If user changed a value, notify other scripts through onUpdateNode
if (EditorGUI.EndChangeCheck()) {
if (onUpdateNode != null) onUpdateNode(target);
}
}
private static FieldInfo[] GetInspectorFields(Node node) {
return node.GetType().GetFields().Where(f => f.IsPublic || f.GetCustomAttributes(typeof(SerializeField), false) != null).ToArray();
}
public virtual int GetWidth() {

View File

@ -164,23 +164,31 @@ public partial class NodeEditorWindow {
NodeEditor nodeEditor = GetNodeEditor(node.GetType());
nodeEditor.target = node;
nodeEditor.serializedObject = new SerializedObject(node);
NodeEditor.portPositions = new Dictionary<NodePort, Vector2>();
//Get node position
Vector2 nodePos = GridToWindowPositionNoClipped(node.position);
//GUIStyle style = (node == selectedNode) ? (GUIStyle)"flow node 0 on" : (GUIStyle)"flow node 0";
GUILayout.BeginArea(new Rect(nodePos, new Vector2(nodeEditor.GetWidth(), 4000)));
GUIStyle style = NodeEditorResources.styles.nodeBody;
GUILayout.BeginVertical(new GUIStyle(style));
EditorGUI.BeginChangeCheck();
//Draw node contents
Dictionary<NodePort, Vector2> portHandlePoints;
nodeEditor.OnNodeGUI(out portHandlePoints);
nodeEditor.OnNodeGUI();
//Apply
nodeEditor.serializedObject.ApplyModifiedProperties();
//If user changed a value, notify other scripts through onUpdateNode
if (EditorGUI.EndChangeCheck()) {
if (NodeEditor.onUpdateNode != null) NodeEditor.onUpdateNode(node);
}
if (e.type == EventType.Repaint) {
foreach (var kvp in portHandlePoints) {
foreach (var kvp in NodeEditor.portPositions) {
Vector2 portHandlePos = kvp.Value;
portHandlePos += node.position;
Rect rect = new Rect(portHandlePos.x - 8, portHandlePos.y - 8, 16, 16);

View File

@ -6,232 +6,53 @@ using System.Reflection;
using UnityEditor;
using UnityEngine;
/// <summary> Provides unec-specific field editors </summary>
public class NodeEditorGUILayout {
/// <summary> UNEC-specific version of <see cref="EditorGUILayout"/> </summary>
public static class NodeEditorGUILayout {
private static double tempValue;
public static void PropertyField(SerializedProperty property, bool includeChildren) {
Node node = property.serializedObject.targetObject as Node;
NodePort port = node.GetPortByFieldName(property.name);
public static object PortField(string label, object value, System.Type type, NodePort port, bool fallback, out Vector2 portPosition) {
if (fallback) value = PropertyField(label, value, type);
else EditorGUILayout.LabelField(label);
float temp_labelWidth = EditorGUIUtility.labelWidth;
Rect rect = GUILayoutUtility.GetLastRect();
if (port == null) { portPosition = rect.position; return null; }
if (port.direction == NodePort.IO.Input) rect.position = rect.position - new Vector2(16, 0);
if (port.direction == NodePort.IO.Output) rect.position = rect.position + new Vector2(rect.width, 0);
rect.size = new Vector2(16, 16);
// If property is not a port, display a regular property field
if (port == null) EditorGUILayout.PropertyField(property, includeChildren);
else {
Rect rect = new Rect();
// If property is an input, display a regular property field and put a port handle on the left side
if (port.direction == NodePort.IO.Input) {
// Display a label if port is connected
if (port.IsConnected) EditorGUILayout.LabelField(property.displayName);
// Display an editable property field if port is not connected
else EditorGUILayout.PropertyField(property, includeChildren);
rect = GUILayoutUtility.GetLastRect();
rect.position = rect.position - new Vector2(16, 0);
// If property is an output, display a text label and put a port handle on the right side
} else if (port.direction == NodePort.IO.Output) {
EditorGUILayout.LabelField(property.displayName, NodeEditorResources.styles.outputPort);
rect = GUILayoutUtility.GetLastRect();
rect.position = rect.position + new Vector2(rect.width, 0);
}
rect.size = new Vector2(16, 16);
DrawPortHandle(rect, port.type);
// Register the handle position
Vector2 portPos = rect.center;
if (NodeEditor.portPositions.ContainsKey(port)) NodeEditor.portPositions[port] = portPos;
else NodeEditor.portPositions.Add(port, portPos);
}
EditorGUIUtility.labelWidth = temp_labelWidth;
}
private static void DrawPortHandle(Rect rect, Type type) {
Color col = GUI.color;
GUI.color = new Color32(90, 97, 105, 255);
GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
GUI.color = NodeEditorPreferences.GetTypeColor(port.type);
GUI.color = NodeEditorPreferences.GetTypeColor(type);
GUI.DrawTexture(rect, NodeEditorResources.dot);
GUI.color = col;
portPosition = rect.center;
return value;
}
public static object PropertyField(Node target, FieldInfo fieldInfo, Dictionary<NodePort, Vector2> portPositions) {
Type fieldType = fieldInfo.FieldType;
string fieldName = fieldInfo.Name;
string fieldPrettyName = fieldName.PrettifyCamelCase();
object fieldValue = fieldInfo.GetValue(target);
object[] fieldAttribs = fieldInfo.GetCustomAttributes(false);
HeaderAttribute headerAttrib;
if (NodeEditorUtilities.GetAttrib(fieldAttribs, out headerAttrib)) {
EditorGUILayout.LabelField(headerAttrib.header);
}
Node.OutputAttribute outputAttrib;
Node.InputAttribute inputAttrib;
EditorGUI.BeginChangeCheck();
if (NodeEditorUtilities.GetAttrib(fieldAttribs, out inputAttrib)) {
NodePort port = target.GetPortByFieldName(fieldName);
Vector2 portPos;
bool fallback = inputAttrib.backingValue == Node.ShowBackingValue.Always || (inputAttrib.backingValue == Node.ShowBackingValue.Unconnected && !port.IsConnected);
fieldValue = PortField(fieldPrettyName, fieldValue, fieldType, port, fallback, out portPos);
portPositions.Add(port, portPos);
} else if (NodeEditorUtilities.GetAttrib(fieldAttribs, out outputAttrib)) {
NodePort port = target.GetPortByFieldName(fieldName);
Vector2 portPos;
fieldValue = PortField(fieldPrettyName, fieldValue, fieldType, port, false, out portPos);
portPositions.Add(port, portPos);
} else {
fieldValue = PropertyField(fieldPrettyName, fieldValue, fieldType);
}
if (EditorGUI.EndChangeCheck()) {
fieldInfo.SetValue(target, fieldValue);
EditorUtility.SetDirty(target);
}
return fieldValue;
}
public static object PropertyField(string label, object value, System.Type type) {
if (type == typeof(int)) return IntField(label, value is int ? (int)value : default(int));
else if (type == typeof(float)) return FloatField(label, value is float ? (float)value : default(float));
else if (type == typeof(double)) return DoubleField(label, value is double ? (double)value : default(double));
else if (type == typeof(long)) return LongField(label, value is long ? (long)value : default(long));
else if (type == typeof(bool)) return Toggle(label, value is bool ? (bool)value : default(bool));
else if (type == typeof(string)) return TextField(label, value is string ? (string)value : default(string));
else if (type == typeof(Rect)) return RectField(label, value is Rect ? (Rect)value : default(Rect));
else if (type == typeof(Vector2)) return Vector2Field(label, value is Vector2 ? (Vector2)value : default(Vector2));
else if (type == typeof(Vector3)) return Vector3Field(label, value is Vector3 ? (Vector3)value : default(Vector3));
else if (type == typeof(Vector4)) return Vector4Field(label, value is Vector4 ? (Vector4)value : default(Vector4));
else if (type == typeof(Color)) return ColorField(label, value is Color ? (Color)value : default(Color));
else if (type == typeof(AnimationCurve)) return CurveField(label, value is AnimationCurve ? (AnimationCurve)value : default(AnimationCurve));
else if (type.IsSubclassOf(typeof(Enum)) || type == typeof(Enum)) return EnumField(label, (Enum)value);
else if (type.IsSubclassOf(typeof(UnityEngine.Object)) || type == typeof(UnityEngine.Object)) return ObjectField(label, (UnityEngine.Object)value, type);
else { GUILayout.Label(label); return value; }
}
public static Rect GetRect(string label) {
Rect rect = EditorGUILayout.GetControlRect();
rect.width *= 0.5f;
EditorGUI.LabelField(rect, label);
rect.x += rect.width;
return rect;
}
public static UnityEngine.Object ObjectField(string label, UnityEngine.Object value, Type type) {
return EditorGUI.ObjectField(GetRect(label), value, type, true);
}
public static AnimationCurve CurveField(string label, AnimationCurve value) {
if (value == null) value = new AnimationCurve();
return EditorGUI.CurveField(GetRect(label), value);
}
public static Color ColorField(string label, Color value) {
return EditorGUI.ColorField(GetRect(label), value);
}
public static Vector4 Vector4Field(string label, Vector4 value) {
return EditorGUILayout.Vector4Field(label, value);
}
public static Vector3 Vector3Field(string label, Vector3 value) {
return EditorGUILayout.Vector3Field(label, value);
}
public static Vector2 Vector2Field(string label, Vector2 value) {
return EditorGUILayout.Vector2Field(label, value);
}
public static Rect RectField(string label, Rect value) {
return EditorGUILayout.RectField(label, value);
}
public static string TextField(string label, string value) {
return EditorGUI.TextField(GetRect(label), value);
}
public static bool Toggle(string label, bool value) {
return EditorGUI.Toggle(GetRect(label), value);
}
public static int IntField(string label, int value) {
GUIUtility.GetControlID(FocusType.Passive);
Rect rect = EditorGUILayout.GetControlRect();
rect.width *= 0.5f;
if (NodeEditorWindow.current != null) {
double v = (double) value;
DragNumber(rect, ref v);
value = (int) v;
if (GUI.changed) NodeEditorWindow.current.Repaint();
}
EditorGUI.LabelField(rect, label);
rect.x += rect.width;
if (!GUI.changed) value = EditorGUI.IntField(rect, value);
else {
EditorGUI.IntField(rect, value);
}
return value;
}
public static float FloatField(string label, float value) {
GUIUtility.GetControlID(FocusType.Passive);
Rect rect = EditorGUILayout.GetControlRect();
rect.width *= 0.5f;
if (NodeEditorWindow.current != null) {
double v = (double) value;
DragNumber(rect, ref v);
value = (float) v;
if (GUI.changed) NodeEditorWindow.current.Repaint();
}
EditorGUI.LabelField(rect, label);
rect.x += rect.width;
if (!GUI.changed) value = EditorGUI.FloatField(rect, value);
else {
EditorGUI.FloatField(rect, value);
}
return value;
}
public static double DoubleField(string label, double value) {
GUIUtility.GetControlID(FocusType.Passive);
Rect rect = EditorGUILayout.GetControlRect();
rect.width *= 0.5f;
if (NodeEditorWindow.current != null) {
double v = (double) value;
DragNumber(rect, ref v);
value = (double) v;
if (GUI.changed) NodeEditorWindow.current.Repaint();
}
EditorGUI.LabelField(rect, label);
rect.x += rect.width;
if (!GUI.changed) value = EditorGUI.DoubleField(rect, value);
else {
EditorGUI.DoubleField(rect, value);
}
return value;
}
public static long LongField(string label, long value) {
GUIUtility.GetControlID(FocusType.Passive);
Rect rect = EditorGUILayout.GetControlRect();
rect.width *= 0.5f;
if (NodeEditorWindow.current != null) {
double v = (double) value;
DragNumber(rect, ref v);
value = (long) v;
if (GUI.changed) NodeEditorWindow.current.Repaint();
}
EditorGUI.LabelField(rect, label);
rect.x += rect.width;
if (!GUI.changed) value = EditorGUI.LongField(rect, value);
else {
EditorGUI.LongField(rect, value);
}
return value;
}
public static void DragNumber(Rect rect, ref double value) {
double sensitivity = Math.Max(0.09432981473891d, Math.Pow(Math.Abs(value), 0.5d) * 0.03d);
int id = GUIUtility.GetControlID(FocusType.Passive);
Event e = Event.current;
switch (e.type) {
case EventType.MouseDown:
if (rect.Contains(e.mousePosition) && e.button == 0) {
tempValue = value;
GUIUtility.hotControl = id;
e.Use();
GUIUtility.keyboardControl = id;
EditorGUIUtility.SetWantsMouseJumping(1);
}
break;
case EventType.MouseUp:
tempValue = 0;
if (GUIUtility.hotControl == id) {
GUIUtility.hotControl = 0;
e.Use();
EditorGUIUtility.SetWantsMouseJumping(0);
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == id) {
tempValue += HandleUtility.niceMouseDelta * sensitivity;
value = tempValue;
GUI.changed = true;
}
break;
case EventType.Repaint:
if (NodeEditorWindow.current != null && Mathf.Approximately(NodeEditorWindow.current.zoom, 1)) {
EditorGUIUtility.AddCursorRect(rect, MouseCursor.SlideArrow);
}
break;
}
}
public static Enum EnumField(string label, Enum value) {
return EditorGUI.EnumPopup(GetRect(label), value);
}
}