diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 015ed0a..631d8e5 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -49,7 +49,7 @@ namespace XNodeEditor { public virtual int GetWidth() { Type type = target.GetType(); int width; - if (NodeEditorWindow.nodeWidth.TryGetValue(type, out width)) return width; + if (type.TryGetAttributeWidth(out width)) return width; else return 208; } @@ -58,7 +58,7 @@ namespace XNodeEditor { // Try get color from [NodeTint] attribute Type type = target.GetType(); Color color; - if (NodeEditorWindow.nodeTint.TryGetValue(type, out color)) return color; + if (type.TryGetAttributeTint(out color)) return color; // Return default color (grey) else return DEFAULTCOLOR; } @@ -84,7 +84,7 @@ namespace XNodeEditor { // Custom sctions if only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; - NodeEditorWindow.AddCustomContextMenuItems(menu, node); + menu.AddCustomContextMenuItems(node); } } diff --git a/Scripts/Editor/NodeEditorBase.cs b/Scripts/Editor/NodeEditorBase.cs index ab463e6..261a284 100644 --- a/Scripts/Editor/NodeEditorBase.cs +++ b/Scripts/Editor/NodeEditorBase.cs @@ -50,7 +50,7 @@ namespace XNodeEditor.Internal { editorTypes = new Dictionary(); //Get all classes deriving from NodeEditor via reflection - Type[] nodeEditors = XNodeEditor.NodeEditorWindow.GetDerivedTypes(typeof(T)); + Type[] nodeEditors = typeof(T).GetDerivedTypes(); for (int i = 0; i < nodeEditors.Length; i++) { if (nodeEditors[i].IsAbstract) continue; var attribs = nodeEditors[i].GetCustomAttributes(typeof(A), false); diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index 7372508..417a454 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -141,7 +141,7 @@ namespace XNodeEditor { Color backgroundColor = new Color32(90, 97, 105, 255); Color tint; - if (NodeEditorWindow.nodeTint.TryGetValue(port.node.GetType(), out tint)) backgroundColor *= tint; + if (port.node.GetType().TryGetAttributeTint(out tint)) backgroundColor *= tint; Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); @@ -153,7 +153,7 @@ namespace XNodeEditor { private static System.Type GetType(SerializedProperty property) { System.Type parentType = property.serializedObject.targetObject.GetType(); - System.Reflection.FieldInfo fi = NodeEditorWindow.GetFieldInfo(parentType, property.name); + System.Reflection.FieldInfo fi = parentType.GetFieldInfo(property.name); return fi.FieldType; } @@ -197,7 +197,7 @@ namespace XNodeEditor { Color backgroundColor = new Color32(90, 97, 105, 255); Color tint; - if (NodeEditorWindow.nodeTint.TryGetValue(port.node.GetType(), out tint)) backgroundColor *= tint; + if (port.node.GetType().TryGetAttributeTint(out tint)) backgroundColor *= tint; Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); @@ -225,7 +225,7 @@ namespace XNodeEditor { Color backgroundColor = new Color32(90, 97, 105, 255); Color tint; - if (NodeEditorWindow.nodeTint.TryGetValue(port.node.GetType(), out tint)) backgroundColor *= tint; + if (port.node.GetType().TryGetAttributeTint(out tint)) backgroundColor *= tint; Color col = NodeEditorWindow.current.graphEditor.GetPortColor(port); DrawPortHandle(rect, backgroundColor, col); diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs index 76ec656..0a0a36a 100644 --- a/Scripts/Editor/NodeEditorReflection.cs +++ b/Scripts/Editor/NodeEditorReflection.cs @@ -7,62 +7,55 @@ using UnityEditor; using UnityEngine; namespace XNodeEditor { - /// Contains reflection-related info - public partial class NodeEditorWindow { - /// Custom node tint colors defined with [NodeColor(r, g, b)] - public static Dictionary nodeTint { get { return _nodeTint != null ? _nodeTint : _nodeTint = GetNodeTint(); } } - - [NonSerialized] private static Dictionary _nodeTint; - /// Custom node widths defined with [NodeWidth(width)] - public static Dictionary nodeWidth { get { return _nodeWidth != null ? _nodeWidth : _nodeWidth = GetNodeWidth(); } } - - [NonSerialized] private static Dictionary _nodeWidth; + /// Contains reflection-related extensions built for xNode + public static class NodeEditorReflection { + [NonSerialized] private static Dictionary nodeTint; + [NonSerialized] private static Dictionary nodeWidth; /// All available node types public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } } [NonSerialized] private static Type[] _nodeTypes = null; - private Func isDocked { - get { - if (_isDocked == null) { - BindingFlags fullBinding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; - MethodInfo isDockedMethod = typeof(NodeEditorWindow).GetProperty("docked", fullBinding).GetGetMethod(true); - _isDocked = (Func) Delegate.CreateDelegate(typeof(Func), this, isDockedMethod); - } - return _isDocked; - } + /// Return a delegate used to determine whether window is docked or not. It is faster to cache this delegate than run the reflection required each time. + public static Func GetIsDockedDelegate(this EditorWindow window) { + BindingFlags fullBinding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + MethodInfo isDockedMethod = typeof(EditorWindow).GetProperty("docked", fullBinding).GetGetMethod(true); + return (Func) Delegate.CreateDelegate(typeof(Func), window, isDockedMethod); } - private Func _isDocked; public static Type[] GetNodeTypes() { //Get all classes deriving from Node via reflection return GetDerivedTypes(typeof(XNode.Node)); } - public static Dictionary GetNodeTint() { - Dictionary tints = new Dictionary(); - for (int i = 0; i < nodeTypes.Length; i++) { - var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.Node.NodeTintAttribute), true); - if (attribs == null || attribs.Length == 0) continue; - XNode.Node.NodeTintAttribute attrib = attribs[0] as XNode.Node.NodeTintAttribute; - tints.Add(nodeTypes[i], attrib.color); + /// Custom node tint colors defined with [NodeColor(r, g, b)] + public static bool TryGetAttributeTint(this Type nodeType, out Color tint) { + if (nodeTint == null) { + CacheAttributes(ref nodeTint, x => x.color); } - return tints; + return nodeTint.TryGetValue(nodeType, out tint); } - public static Dictionary GetNodeWidth() { - Dictionary widths = new Dictionary(); - for (int i = 0; i < nodeTypes.Length; i++) { - var attribs = nodeTypes[i].GetCustomAttributes(typeof(XNode.Node.NodeWidthAttribute), true); - if (attribs == null || attribs.Length == 0) continue; - XNode.Node.NodeWidthAttribute attrib = attribs[0] as XNode.Node.NodeWidthAttribute; - widths.Add(nodeTypes[i], attrib.width); + /// Get custom node widths defined with [NodeWidth(width)] + public static bool TryGetAttributeWidth(this Type nodeType, out int width) { + if (nodeWidth == null) { + CacheAttributes(ref nodeWidth, x => x.width); + } + return nodeWidth.TryGetValue(nodeType, out width); + } + + private static void CacheAttributes(ref Dictionary dict, Func getter) where A : Attribute { + dict = new Dictionary(); + for (int i = 0; i < nodeTypes.Length; i++) { + object[] attribs = nodeTypes[i].GetCustomAttributes(typeof(A), true); + if (attribs == null || attribs.Length == 0) continue; + A attrib = attribs[0] as A; + dict.Add(nodeTypes[i], getter(attrib)); } - return widths; } /// Get FieldInfo of a field, including those that are private and/or inherited - public static FieldInfo GetFieldInfo(Type type, string fieldName) { + public static FieldInfo GetFieldInfo(this Type type, string fieldName) { // If we can't find field in the first run, it's probably a private field in a base class. FieldInfo field = type.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // Search base classes for private fields only. Public fields are found above @@ -71,7 +64,7 @@ namespace XNodeEditor { } /// Get all classes deriving from baseType via reflection - public static Type[] GetDerivedTypes(Type baseType) { + public static Type[] GetDerivedTypes(this Type baseType) { List types = new List(); System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { @@ -82,7 +75,8 @@ namespace XNodeEditor { return types.ToArray(); } - public static void AddCustomContextMenuItems(GenericMenu contextMenu, object obj) { + /// Find methods marked with the [ContextMenu] attribute and add them to the context menu + public static void AddCustomContextMenuItems(this GenericMenu contextMenu, object obj) { KeyValuePair[] items = GetContextMenuMethods(obj); if (items.Length != 0) { contextMenu.AddSeparator(""); @@ -104,7 +98,7 @@ namespace XNodeEditor { } /// Call OnValidate on target - public static void TriggerOnValidate(UnityEngine.Object target) { + public static void TriggerOnValidate(this UnityEngine.Object target) { System.Reflection.MethodInfo onValidate = null; if (target != null) { onValidate = target.GetType().GetMethod("OnValidate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index 77e71bb..f45a042 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -36,7 +36,7 @@ namespace XNodeEditor { public static bool GetAttrib(Type classType, string fieldName, out T attribOut) where T : Attribute { // If we can't find field in the first run, it's probably a private field in a base class. - FieldInfo field = NodeEditorWindow.GetFieldInfo(classType, fieldName); + FieldInfo field = classType.GetFieldInfo(fieldName); // This shouldn't happen. Ever. if (field == null) { Debug.LogWarning("Field " + fieldName + " couldnt be found"); diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs index fc04439..a063410 100644 --- a/Scripts/Editor/NodeEditorWindow.cs +++ b/Scripts/Editor/NodeEditorWindow.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using UnityEditor; using UnityEditor.Callbacks; using UnityEngine; +using System; +using Object = UnityEngine.Object; namespace XNodeEditor { [InitializeOnLoad] @@ -14,6 +16,14 @@ namespace XNodeEditor { [SerializeField] private NodePortReference[] _references = new NodePortReference[0]; [SerializeField] private Rect[] _rects = new Rect[0]; + private Func isDocked { + get { + if (_isDocked == null) _isDocked = this.GetIsDockedDelegate(); + return _isDocked; + } + } + private Func _isDocked; + [System.Serializable] private class NodePortReference { [SerializeField] private XNode.Node _node; [SerializeField] private string _name; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index ff2ca5c..110c506 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -44,8 +44,8 @@ namespace XNodeEditor { /// Add items for the context menu when right-clicking this node. Override to add custom menu items. public virtual void AddContextMenuItems(GenericMenu menu) { Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); - for (int i = 0; i < NodeEditorWindow.nodeTypes.Length; i++) { - Type type = NodeEditorWindow.nodeTypes[i]; + for (int i = 0; i < NodeEditorReflection.nodeTypes.Length; i++) { + Type type = NodeEditorReflection.nodeTypes[i]; //Get node context menu path string path = GetNodeMenuName(type); @@ -58,8 +58,8 @@ namespace XNodeEditor { menu.AddSeparator(""); if (NodeEditorWindow.copyBuffer != null && NodeEditorWindow.copyBuffer.Length > 0) menu.AddItem(new GUIContent("Paste"), false, () => NodeEditorWindow.current.PasteNodes(pos)); else menu.AddDisabledItem(new GUIContent("Paste")); - menu.AddItem(new GUIContent("Preferences"), false, () => NodeEditorWindow.OpenPreferences()); - NodeEditorWindow.AddCustomContextMenuItems(menu, target); + menu.AddItem(new GUIContent("Preferences"), false, () => NodeEditorReflection.OpenPreferences()); + menu.AddCustomContextMenuItems(target); } public virtual Color GetPortColor(XNode.NodePort port) { diff --git a/Scripts/Editor/RenamePopup.cs b/Scripts/Editor/RenamePopup.cs index 4903525..564374e 100644 --- a/Scripts/Editor/RenamePopup.cs +++ b/Scripts/Editor/RenamePopup.cs @@ -51,7 +51,7 @@ namespace XNodeEditor { target.name = NodeEditorUtilities.NodeDefaultName(target.GetType()); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); - NodeEditorWindow.TriggerOnValidate(target); + target.TriggerOnValidate(); } } // Rename asset to input text @@ -60,7 +60,7 @@ namespace XNodeEditor { target.name = input; AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(target)); Close(); - NodeEditorWindow.TriggerOnValidate(target); + target.TriggerOnValidate(); } } }