Skip to content

Commit

Permalink
dbrizov#113 added perf boost with the help of caching the expensive r…
Browse files Browse the repository at this point in the history
…eflection
  • Loading branch information
niggo1243 committed Sep 6, 2021
1 parent 2b0bb4e commit 1b8001c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 61 deletions.
109 changes: 78 additions & 31 deletions Assets/NaughtyAttributes/Scripts/Editor/NaughtyInspector.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
using System.Collections.Generic;
using System;
using System.Linq;
using System.Reflection;

using UnityEditor;
using UnityEngine;

namespace NaughtyAttributes.Editor
{
public class NaughtyProperty
{
public SerializedProperty property;
public SpecialCaseDrawerAttribute specialCaseDrawerAttribute;
public ShowIfAttributeBase showIfAttribute;

public EnableIfAttributeBase enableIfAttribute;

public ReadOnlyAttribute readOnlyAttribute;

public ValidatorAttribute[] validatorAttributes;
}

[CanEditMultipleObjects]
[CustomEditor(typeof(UnityEngine.Object), true)]
public class NaughtyInspector : UnityEditor.Editor
{
private List<SerializedProperty> _serializedProperties = new List<SerializedProperty>();
private List<NaughtyProperty> _serializedProperties = new List<NaughtyProperty>();
private IEnumerable<FieldInfo> _nonSerializedFields;
private IEnumerable<PropertyInfo> _nativeProperties;
private IEnumerable<MethodInfo> _methods;

private IEnumerable<NaughtyProperty> _nonGroupedSerializedProperty;

private SerializedProperty m_ScriptProperty;

private IEnumerable<IGrouping<string, NaughtyProperty>> _groupedSerialzedProperty;

private IEnumerable<IGrouping<string, NaughtyProperty>> _foldoutGroupedSerializedProperty;

private Dictionary<string, SavedBool> _foldouts = new Dictionary<string, SavedBool>();

private bool _anyNaughtyAttribute;

protected virtual void OnEnable()
{
_nonSerializedFields = ReflectionUtility.GetAllFields(
Expand All @@ -26,19 +52,30 @@ protected virtual void OnEnable()

_methods = ReflectionUtility.GetAllMethods(
target, m => m.GetCustomAttributes(typeof(ButtonAttribute), true).Length > 0);

GetSerializedProperties(ref _serializedProperties);

_anyNaughtyAttribute = _serializedProperties.Any(p => PropertyUtility.GetAttribute<INaughtyAttribute>(p.property) != null);

_nonGroupedSerializedProperty = GetNonGroupedProperties(_serializedProperties);
NaughtyProperty[] mScripts = _serializedProperties.Where(p => p.property.name.Equals("m_Script")).ToArray();

m_ScriptProperty = mScripts.Length > 0 ? mScripts[0].property : null;

_groupedSerialzedProperty = GetGroupedProperties(_serializedProperties);

_foldoutGroupedSerializedProperty = GetFoldoutProperties(_serializedProperties);
}

protected virtual void OnDisable()
{
ReorderableListPropertyDrawer.Instance.ClearCache();
}


public override void OnInspectorGUI()
{
GetSerializedProperties(ref _serializedProperties);

bool anyNaughtyAttribute = _serializedProperties.Any(p => PropertyUtility.GetAttribute<INaughtyAttribute>(p) != null);
if (!anyNaughtyAttribute)
if (!_anyNaughtyAttribute)
{
DrawDefaultInspector();
}
Expand All @@ -51,8 +88,8 @@ public override void OnInspectorGUI()
DrawNativeProperties();
DrawButtons();
}

protected void GetSerializedProperties(ref List<SerializedProperty> outSerializedProperties)
protected void GetSerializedProperties(ref List<NaughtyProperty> outSerializedProperties)
{
outSerializedProperties.Clear();
using (var iterator = serializedObject.GetIterator())
Expand All @@ -61,7 +98,19 @@ protected void GetSerializedProperties(ref List<SerializedProperty> outSerialize
{
do
{
outSerializedProperties.Add(serializedObject.FindProperty(iterator.name));
NaughtyProperty naughtyProperty = new NaughtyProperty();
naughtyProperty.property = serializedObject.FindProperty(iterator.name);

naughtyProperty.readOnlyAttribute = PropertyUtility.GetAttribute<ReadOnlyAttribute>(naughtyProperty.property);
naughtyProperty.enableIfAttribute = PropertyUtility.GetAttribute<EnableIfAttributeBase>(naughtyProperty.property);

naughtyProperty.showIfAttribute = PropertyUtility.GetAttribute<ShowIfAttributeBase>(naughtyProperty.property);
naughtyProperty.validatorAttributes = PropertyUtility.GetAttributes<ValidatorAttribute>(naughtyProperty.property);

naughtyProperty.specialCaseDrawerAttribute =
PropertyUtility.GetAttribute<SpecialCaseDrawerAttribute>(naughtyProperty.property);

outSerializedProperties.Add(naughtyProperty);
}
while (iterator.NextVisible(false));
}
Expand All @@ -72,26 +121,24 @@ protected void DrawSerializedProperties()
{
serializedObject.Update();

// Draw non-grouped serialized properties
foreach (var property in GetNonGroupedProperties(_serializedProperties))
if (m_ScriptProperty != null)
{
if (property.name.Equals("m_Script", System.StringComparison.Ordinal))
using (new EditorGUI.DisabledScope(disabled: true))
{
using (new EditorGUI.DisabledScope(disabled: true))
{
EditorGUILayout.PropertyField(property);
}
}
else
{
NaughtyEditorGUI.PropertyField_Layout(property, includeChildren: true);
EditorGUILayout.PropertyField(m_ScriptProperty);
}
}

// Draw non-grouped serialized properties
foreach (var property in _nonGroupedSerializedProperty)
{
NaughtyEditorGUI.PropertyField_Layout(property, includeChildren: true);
}

// Draw grouped serialized properties
foreach (var group in GetGroupedProperties(_serializedProperties))
foreach (var group in _groupedSerialzedProperty)
{
IEnumerable<SerializedProperty> visibleProperties = group.Where(p => PropertyUtility.IsVisible(p));
IEnumerable<NaughtyProperty> visibleProperties = group.Where(p => PropertyUtility.IsVisible(p.property));
if (!visibleProperties.Any())
{
continue;
Expand All @@ -107,9 +154,9 @@ protected void DrawSerializedProperties()
}

// Draw foldout serialized properties
foreach (var group in GetFoldoutProperties(_serializedProperties))
foreach (var group in _foldoutGroupedSerializedProperty)
{
IEnumerable<SerializedProperty> visibleProperties = group.Where(p => PropertyUtility.IsVisible(p));
IEnumerable<NaughtyProperty> visibleProperties = group.Where(p => PropertyUtility.IsVisible(p.property));
if (!visibleProperties.Any())
{
continue;
Expand Down Expand Up @@ -190,23 +237,23 @@ protected void DrawButtons(bool drawHeader = false)
}
}

private static IEnumerable<SerializedProperty> GetNonGroupedProperties(IEnumerable<SerializedProperty> properties)
private static IEnumerable<NaughtyProperty> GetNonGroupedProperties(IEnumerable<NaughtyProperty> properties)
{
return properties.Where(p => PropertyUtility.GetAttribute<IGroupAttribute>(p) == null);
return properties.Where(p => PropertyUtility.GetAttribute<IGroupAttribute>(p.property) == null && !p.property.name.Equals("m_Script"));
}

private static IEnumerable<IGrouping<string, SerializedProperty>> GetGroupedProperties(IEnumerable<SerializedProperty> properties)
private static IEnumerable<IGrouping<string, NaughtyProperty>> GetGroupedProperties(IEnumerable<NaughtyProperty> properties)
{
return properties
.Where(p => PropertyUtility.GetAttribute<BoxGroupAttribute>(p) != null)
.GroupBy(p => PropertyUtility.GetAttribute<BoxGroupAttribute>(p).Name);
.Where(p => PropertyUtility.GetAttribute<BoxGroupAttribute>(p.property) != null)
.GroupBy(p => PropertyUtility.GetAttribute<BoxGroupAttribute>(p.property).Name);
}

private static IEnumerable<IGrouping<string, SerializedProperty>> GetFoldoutProperties(IEnumerable<SerializedProperty> properties)
private static IEnumerable<IGrouping<string, NaughtyProperty>> GetFoldoutProperties(IEnumerable<NaughtyProperty> properties)
{
return properties
.Where(p => PropertyUtility.GetAttribute<FoldoutAttribute>(p) != null)
.GroupBy(p => PropertyUtility.GetAttribute<FoldoutAttribute>(p).Name);
.Where(p => PropertyUtility.GetAttribute<FoldoutAttribute>(p.property) != null)
.GroupBy(p => PropertyUtility.GetAttribute<FoldoutAttribute>(p.property).Name);
}

private static GUIStyle GetHeaderGUIStyle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private void DrawChildProperties(Rect rect, SerializedProperty property)
height = childHeight
};

NaughtyEditorGUI.PropertyField(childRect, childProperty, true);
NaughtyEditorGUI.PropertyField(childRect, new NaughtyProperty(){property=childProperty}, true);

yOffset += childHeight;
}
Expand Down
43 changes: 21 additions & 22 deletions Assets/NaughtyAttributes/Scripts/Editor/Utility/NaughtyEditorGUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Linq;
using System.Reflection;

using UnityEditor;
using UnityEditor.Experimental.SceneManagement;
using UnityEditor.SceneManagement;
Expand All @@ -16,65 +17,63 @@ public static class NaughtyEditorGUI

private static GUIStyle _buttonStyle = new GUIStyle(GUI.skin.button) { richText = true };

private delegate void PropertyFieldFunction(Rect rect, SerializedProperty property, GUIContent label, bool includeChildren);
private delegate void PropertyFieldFunction(Rect rect, NaughtyProperty property, GUIContent label, bool includeChildren);

public static void PropertyField(Rect rect, SerializedProperty property, bool includeChildren)
public static void PropertyField(Rect rect, NaughtyProperty property, bool includeChildren)
{
PropertyField_Implementation(rect, property, includeChildren, DrawPropertyField);
}

public static void PropertyField_Layout(SerializedProperty property, bool includeChildren)
public static void PropertyField_Layout(NaughtyProperty property, bool includeChildren)
{
Rect dummyRect = new Rect();
PropertyField_Implementation(dummyRect, property, includeChildren, DrawPropertyField_Layout);
}

private static void DrawPropertyField(Rect rect, SerializedProperty property, GUIContent label, bool includeChildren)
private static void DrawPropertyField(Rect rect, NaughtyProperty property, GUIContent label, bool includeChildren)
{
EditorGUI.PropertyField(rect, property, label, includeChildren);
EditorGUI.PropertyField(rect, property.property, label, includeChildren);
}

private static void DrawPropertyField_Layout(Rect rect, SerializedProperty property, GUIContent label, bool includeChildren)
private static void DrawPropertyField_Layout(Rect rect, NaughtyProperty property, GUIContent label, bool includeChildren)
{
EditorGUILayout.PropertyField(property, label, includeChildren);
EditorGUILayout.PropertyField(property.property, label, includeChildren);
}

private static void PropertyField_Implementation(Rect rect, SerializedProperty property, bool includeChildren, PropertyFieldFunction propertyFieldFunction)
private static void PropertyField_Implementation(Rect rect, NaughtyProperty property, bool includeChildren, PropertyFieldFunction propertyFieldFunction)
{
SpecialCaseDrawerAttribute specialCaseAttribute = PropertyUtility.GetAttribute<SpecialCaseDrawerAttribute>(property);
if (specialCaseAttribute != null)
if (property.specialCaseDrawerAttribute != null)
{
specialCaseAttribute.GetDrawer().OnGUI(rect, property);
property.specialCaseDrawerAttribute.GetDrawer().OnGUI(rect, property.property);
}
else
{
// Check if visible
bool visible = PropertyUtility.IsVisible(property);
bool visible = PropertyUtility.IsVisible(property.showIfAttribute, property.property);
if (!visible)
{
return;
}

// Validate
ValidatorAttribute[] validatorAttributes = PropertyUtility.GetAttributes<ValidatorAttribute>(property);
foreach (var validatorAttribute in validatorAttributes)
foreach (var validatorAttribute in property.validatorAttributes)
{
validatorAttribute.GetValidator().ValidateProperty(property);
validatorAttribute.GetValidator().ValidateProperty(property.property);
}

// Check if enabled and draw
EditorGUI.BeginChangeCheck();
bool enabled = PropertyUtility.IsEnabled(property);

bool enabled = PropertyUtility.IsEnabled(property.readOnlyAttribute, property.enableIfAttribute, property.property);
using (new EditorGUI.DisabledScope(disabled: !enabled))
{
propertyFieldFunction.Invoke(rect, property, PropertyUtility.GetLabel(property), includeChildren);
propertyFieldFunction.Invoke(rect, property, PropertyUtility.GetLabel(property.property), includeChildren);
}

// Call OnValueChanged callbacks
if (EditorGUI.EndChangeCheck())
{
PropertyUtility.CallOnValueChangedCallbacks(property);
PropertyUtility.CallOnValueChangedCallbacks(property.property);
}
}
}
Expand Down
29 changes: 22 additions & 7 deletions Assets/NaughtyAttributes/Scripts/Editor/Utility/PropertyUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,23 @@ public static void CallOnValueChangedCallbacks(SerializedProperty property)
public static bool IsEnabled(SerializedProperty property)
{
ReadOnlyAttribute readOnlyAttribute = GetAttribute<ReadOnlyAttribute>(property);
EnableIfAttributeBase enableIfAttribute = GetAttribute<EnableIfAttributeBase>(property);

return IsEnabled(readOnlyAttribute, enableIfAttribute, property);
}

public static bool IsEnabled(ReadOnlyAttribute readOnlyAttribute, EnableIfAttributeBase enableIfAttribute, SerializedProperty property)
{
if (readOnlyAttribute != null)
{
return false;
}

EnableIfAttributeBase enableIfAttribute = GetAttribute<EnableIfAttributeBase>(property);

if (enableIfAttribute == null)
{
return true;
}

object target = GetTargetObjectWithProperty(property);

// deal with enum conditions
Expand Down Expand Up @@ -118,10 +124,16 @@ public static bool IsEnabled(SerializedProperty property)
return false;
}
}

public static bool IsVisible(SerializedProperty property)
{
ShowIfAttributeBase showIfAttribute = GetAttribute<ShowIfAttributeBase>(property);

return IsVisible(showIfAttribute, property);
}

public static bool IsVisible(ShowIfAttributeBase showIfAttribute, SerializedProperty property)
{
if (showIfAttribute == null)
{
return true;
Expand All @@ -142,7 +154,8 @@ public static bool IsVisible(SerializedProperty property)
return matched != showIfAttribute.Inverted;
}

string message = showIfAttribute.GetType().Name + " needs a valid enum field, property or method name to work";
string message = showIfAttribute.GetType().Name +
" needs a valid enum field, property or method name to work";
Debug.LogWarning(message, property.serializedObject.targetObject);

return false;
Expand All @@ -152,12 +165,14 @@ public static bool IsVisible(SerializedProperty property)
List<bool> conditionValues = GetConditionValues(target, showIfAttribute.Conditions);
if (conditionValues.Count > 0)
{
bool enabled = GetConditionsFlag(conditionValues, showIfAttribute.ConditionOperator, showIfAttribute.Inverted);
bool enabled = GetConditionsFlag(conditionValues, showIfAttribute.ConditionOperator,
showIfAttribute.Inverted);
return enabled;
}
else
{
string message = showIfAttribute.GetType().Name + " needs a valid boolean condition field, property or method name to work";
string message = showIfAttribute.GetType().Name +
" needs a valid boolean condition field, property or method name to work";
Debug.LogWarning(message, property.serializedObject.targetObject);

return false;
Expand Down

0 comments on commit 1b8001c

Please sign in to comment.