#if UNITY_EDITOR using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using Object = UnityEngine.Object; namespace Jovian.InspectorTools.EditorTools { public static class MySerializedProperty { #region Collections Handling /// /// Get array of property childs, if parent property is array /// public static SerializedProperty[] AsArray(this SerializedProperty property) { List items = new List(); for(int i = 0; i < property.arraySize; i++) items.Add(property.GetArrayElementAtIndex(i)); return items.ToArray(); } /// /// Get array of property childs casted to specific type /// public static T[] AsArray(this SerializedProperty property) { var propertiesArray = property.AsArray(); return propertiesArray.Select(s => s.objectReferenceValue).OfType().ToArray(); } /// /// Get array of property childs, if parent property is array /// public static IEnumerable AsIEnumerable(this SerializedProperty property) { for(int i = 0; i < property.arraySize; i++) yield return property.GetArrayElementAtIndex(i); } /// /// Replace array contents of SerializedProperty with another array /// public static void ReplaceArray(this SerializedProperty property, Object[] newElements) { property.arraySize = 0; property.serializedObject.ApplyModifiedProperties(); property.arraySize = newElements.Length; for(var i = 0; i < newElements.Length; i++) { property.GetArrayElementAtIndex(i).objectReferenceValue = newElements[i]; } property.serializedObject.ApplyModifiedProperties(); } /// /// If property is array, insert new element at the end and get it as a property /// public static SerializedProperty NewElement(this SerializedProperty property) { int newElementIndex = property.arraySize; property.InsertArrayElementAtIndex(newElementIndex); return property.GetArrayElementAtIndex(newElementIndex); } #endregion /// /// Property is float, int, vector or int vector /// public static bool IsNumerical(this SerializedProperty property) { var propertyType = property.propertyType; switch(propertyType) { case SerializedPropertyType.Float: case SerializedPropertyType.Integer: case SerializedPropertyType.Vector2: case SerializedPropertyType.Vector3: case SerializedPropertyType.Vector4: case SerializedPropertyType.Vector2Int: case SerializedPropertyType.Vector3Int: return true; default: return false; } } /// /// Get string representation of serialized property /// public static string AsStringValue(this SerializedProperty property) { switch(property.propertyType) { case SerializedPropertyType.String: return property.stringValue; case SerializedPropertyType.Character: case SerializedPropertyType.Integer: if(property.type == "char") return Convert.ToChar(property.intValue).ToString(); return property.intValue.ToString(); case SerializedPropertyType.ObjectReference: return property.objectReferenceValue != null ? property.objectReferenceValue.ToString() : "null"; case SerializedPropertyType.Boolean: return property.boolValue.ToString(); case SerializedPropertyType.Enum: return property.GetValue().ToString(); default: return string.Empty; } } /// /// Property path for collection without ".Array.data[x]" in it /// public static string GetFixedPropertyPath(this SerializedProperty property) => property.propertyPath.Replace(".Array.data[", "["); /// /// Get FieldInfo out of SerializedProperty /// public static FieldInfo GetFieldInfo(this SerializedProperty property) { var targetObject = property.serializedObject.targetObject; var targetType = targetObject.GetType(); return targetType.GetField(property.propertyPath); } /// /// Get raw object value out of the SerializedProperty /// public static object GetValue(this SerializedProperty property) { if(property == null) return null; object obj = property.serializedObject.targetObject; var elements = property.GetFixedPropertyPath().Split('.'); foreach(var element in elements) { if(element.Contains("[")) { var elementName = element.Substring(0, element.IndexOf("[", StringComparison.Ordinal)); var index = Convert.ToInt32(element.Substring(element.IndexOf("[", StringComparison.Ordinal)).Replace("[", "").Replace("]", "")); obj = GetValueByArrayFieldName(obj, elementName, index); } else obj = GetValueByFieldName(obj, element); } return obj; object GetValueByArrayFieldName(object source, string name, int index) { if(!(GetValueByFieldName(source, name) is IEnumerable enumerable)) return null; var enumerator = enumerable.GetEnumerator(); for(var i = 0; i <= index; i++) if(!enumerator.MoveNext()) return null; return enumerator.Current; } // Search "source" object for a field with "name" and get it's value object GetValueByFieldName(object source, string name) { if(source == null) return null; var type = source.GetType(); while(type != null) { var fieldInfo = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if(fieldInfo != null) return fieldInfo.GetValue(source); var propertyInfo = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if(propertyInfo != null) return propertyInfo.GetValue(source, null); type = type.BaseType; } return null; } } /// /// Set raw object value to the SerializedProperty /// public static void SetValue(this SerializedProperty property, object value) { GetFieldInfo(property).SetValue(property.serializedObject.targetObject, value); } /// /// Is specific attribute defined on SerializedProperty /// /// /// /// public static bool IsAttributeDefined(this SerializedProperty property) where T : Attribute { var fieldInfo = property.GetFieldInfo(); if(fieldInfo == null) return false; return Attribute.IsDefined(fieldInfo, typeof(T)); } #region SerializedProperty Get Parent // Found here http://answers.unity.com/answers/425602/view.html // Update here https://gist.github.com/AdrienVR/1548a145c039d2fddf030ebc22f915de to support inherited private members. /// /// Get parent object of SerializedProperty /// public static object GetParent(this SerializedProperty prop) { var path = prop.propertyPath.Replace(".Array.data[", "["); object obj = prop.serializedObject.targetObject; var elements = path.Split('.'); foreach(var element in elements.Take(elements.Length - 1)) { if(element.Contains("[")) { var elementName = element.Substring(0, element.IndexOf("[", StringComparison.Ordinal)); var index = Convert.ToInt32(element.Substring(element.IndexOf("[", StringComparison.Ordinal)).Replace("[", "").Replace("]", "")); obj = GetValueAt(obj, elementName, index); } else { obj = GetValue(obj, element); } } return obj; } private static object GetValue(object source, string name) { if(source == null) return null; foreach(var type in GetHierarchyTypes(source.GetType())) { var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if(f != null) return f.GetValue(source); var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if(p != null) return p.GetValue(source, null); } return null; } private static IEnumerable GetHierarchyTypes(Type sourceType) { yield return sourceType; while(sourceType.BaseType != null) { yield return sourceType.BaseType; sourceType = sourceType.BaseType; } } private static object GetValueAt(object source, string name, int index) { var enumerable = GetValue(source, name) as IEnumerable; if(enumerable == null) return null; var enm = enumerable.GetEnumerator(); while(index-- >= 0) enm.MoveNext(); return enm.Current; } #endregion } } #endif