using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine; namespace NaughtyAttributes.Editor { [CanEditMultipleObjects] [CustomEditor(typeof(UnityEngine.Object), true)] public class NaughtyInspector : UnityEditor.Editor { protected List _serializedProperties = new List(); protected List _nonSerializedFields; protected List _nativeProperties; protected List _methods; protected List _nonGroupedSerializedProperty; protected SerializedProperty m_ScriptProperty; protected List> _groupedSerialzedProperty; protected List> _foldoutGroupedSerializedProperty; private Dictionary _foldouts = new Dictionary(); private bool _anyNaughtyAttribute; protected bool _useCachedMetaAttributes; protected bool _changeDetected; protected virtual void OnEnable() { this.Prepare(); } protected virtual void OnDisable() { //cleanup memory ReorderableListPropertyDrawer.Instance.ClearCache(); _nonSerializedFields.Clear(); _nativeProperties.Clear(); _methods.Clear(); _foldouts.Clear(); _foldoutGroupedSerializedProperty.Clear(); _groupedSerialzedProperty.Clear(); _nonGroupedSerializedProperty.Clear(); _serializedProperties.Clear(); m_ScriptProperty = default; } public virtual void Prepare() { _nonSerializedFields = ReflectionUtility.GetAllFields( target, f => f.GetCustomAttributes(typeof(ShowNonSerializedFieldAttribute), true).Length > 0).ToList(); _nativeProperties = ReflectionUtility.GetAllProperties( target, p => p.GetCustomAttributes(typeof(ShowNativePropertyAttribute), true).Length > 0).ToList(); _methods = ReflectionUtility.GetAllMethods( target, m => m.GetCustomAttributes(typeof(ButtonAttribute), true).Length > 0).ToList(); GetSerializedProperties(ref _serializedProperties); _anyNaughtyAttribute = _serializedProperties.Any(p => PropertyUtility.GetAttribute(p.serializedProperty) != null); _nonGroupedSerializedProperty = GetNonGroupedProperties(_serializedProperties).ToList(); //.First(...) doesnt work for some reason because the m_Script field isnt loaded yet I assume NaughtyProperty[] mScripts = _serializedProperties.Where(p => p.serializedProperty.name.Equals("m_Script")).ToArray(); m_ScriptProperty = mScripts.Length > 0 ? mScripts[0].serializedProperty : null; _groupedSerialzedProperty = GetGroupedProperties(_serializedProperties).ToList(); _foldoutGroupedSerializedProperty = GetFoldoutProperties(_serializedProperties).ToList(); _useCachedMetaAttributes = false; } public override void OnInspectorGUI() { _changeDetected = false; if (!_anyNaughtyAttribute) { DrawDefaultInspector(); } else { DrawSerializedProperties(); } DrawNonSerializedFields(); DrawNativeProperties(); DrawButtons(); _useCachedMetaAttributes = !_changeDetected; } protected virtual void GetSerializedProperties(ref List outSerializedProperties) { outSerializedProperties.Clear(); outSerializedProperties.TrimExcess(); using (var iterator = serializedObject.GetIterator()) { if (iterator.NextVisible(true)) { do { outSerializedProperties.Add( PropertyUtility.CreateNaughtyProperty( serializedObject.FindProperty(iterator.name))); } while (iterator.NextVisible(false)); } } } protected virtual void DrawSerializedProperties() { serializedObject.Update(); if (m_ScriptProperty != null) { using (new EditorGUI.DisabledScope(disabled: true)) { EditorGUILayout.PropertyField(m_ScriptProperty); } } // Draw non-grouped serialized properties foreach (var naughtyProperty in _nonGroupedSerializedProperty) { if (!_useCachedMetaAttributes) { naughtyProperty.cachedIsVisible = PropertyUtility.IsVisible(naughtyProperty.showIfAttribute, naughtyProperty.serializedProperty); naughtyProperty.cachedIsEnabled = PropertyUtility.IsEnabled(naughtyProperty.readOnlyAttribute, naughtyProperty.enableIfAttribute, naughtyProperty.serializedProperty); } _changeDetected |= NaughtyEditorGUI.PropertyField_Layout(naughtyProperty, includeChildren: true); } // Draw grouped serialized properties foreach (var group in _groupedSerialzedProperty) { IEnumerable visibleProperties = _useCachedMetaAttributes ? group.Where(p => p.cachedIsVisible) : group.Where(p => { p.cachedIsEnabled = PropertyUtility.IsEnabled(p.readOnlyAttribute, p.enableIfAttribute, p.serializedProperty); return p.cachedIsVisible = PropertyUtility.IsVisible(p.showIfAttribute, p.serializedProperty); }); if (!visibleProperties.Any()) { continue; } NaughtyEditorGUI.BeginBoxGroup_Layout(group.Key); foreach (var naughtyProperty in visibleProperties) { _changeDetected |= NaughtyEditorGUI.PropertyField_Layout(naughtyProperty, includeChildren: true); } NaughtyEditorGUI.EndBoxGroup_Layout(); } // Draw foldout serialized properties foreach (var group in _foldoutGroupedSerializedProperty) { IEnumerable visibleProperties = _useCachedMetaAttributes ? group.Where(p => p.cachedIsVisible) : group.Where(p => { p.cachedIsEnabled = PropertyUtility.IsEnabled(p.readOnlyAttribute, p.enableIfAttribute, p.serializedProperty); return p.cachedIsVisible = PropertyUtility.IsVisible(p.showIfAttribute, p.serializedProperty); }); if (!visibleProperties.Any()) { continue; } if (!_foldouts.ContainsKey(group.Key)) { _foldouts[group.Key] = new SavedBool($"{target.GetInstanceID()}.{group.Key}", false); } _foldouts[group.Key].Value = EditorGUILayout.Foldout(_foldouts[group.Key].Value, group.Key, true); if (_foldouts[group.Key].Value) { foreach (var naughtyProperty in visibleProperties) { _changeDetected |= NaughtyEditorGUI.PropertyField_Layout(naughtyProperty, true); } } } serializedObject.ApplyModifiedProperties(); } protected virtual void DrawNonSerializedFields(bool drawHeader = false) { if (_nonSerializedFields.Any()) { if (drawHeader) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Non-Serialized Fields", GetHeaderGUIStyle()); NaughtyEditorGUI.HorizontalLine( EditorGUILayout.GetControlRect(false), HorizontalLineAttribute.DefaultHeight, HorizontalLineAttribute.DefaultColor.GetColor()); } foreach (var field in _nonSerializedFields) { NaughtyEditorGUI.NonSerializedField_Layout(serializedObject.targetObject, field); } } } protected virtual void DrawNativeProperties(bool drawHeader = false) { if (_nativeProperties.Any()) { if (drawHeader) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Native Properties", GetHeaderGUIStyle()); NaughtyEditorGUI.HorizontalLine( EditorGUILayout.GetControlRect(false), HorizontalLineAttribute.DefaultHeight, HorizontalLineAttribute.DefaultColor.GetColor()); } foreach (var property in _nativeProperties) { NaughtyEditorGUI.NativeProperty_Layout(serializedObject.targetObject, property); } } } protected virtual void DrawButtons(bool drawHeader = false) { if (_methods.Any()) { if (drawHeader) { EditorGUILayout.Space(); EditorGUILayout.LabelField("Buttons", GetHeaderGUIStyle()); NaughtyEditorGUI.HorizontalLine( EditorGUILayout.GetControlRect(false), HorizontalLineAttribute.DefaultHeight, HorizontalLineAttribute.DefaultColor.GetColor()); } foreach (var method in _methods) { NaughtyEditorGUI.Button(serializedObject.targetObject, method); } } } private static IEnumerable GetNonGroupedProperties(IEnumerable properties) { return properties.Where(p => PropertyUtility.GetAttribute(p.serializedProperty) == null && !p.serializedProperty.name.Equals("m_Script")); } private static IEnumerable> GetGroupedProperties(IEnumerable properties) { return properties .Where(p => PropertyUtility.GetAttribute(p.serializedProperty) != null) .GroupBy(p => PropertyUtility.GetAttribute(p.serializedProperty).Name); } private static IEnumerable> GetFoldoutProperties(IEnumerable properties) { return properties .Where(p => PropertyUtility.GetAttribute(p.serializedProperty) != null) .GroupBy(p => PropertyUtility.GetAttribute(p.serializedProperty).Name); } private static GUIStyle GetHeaderGUIStyle() { GUIStyle style = new GUIStyle(EditorStyles.centeredGreyMiniLabel); style.fontStyle = FontStyle.Bold; style.alignment = TextAnchor.UpperCenter; return style; } } }