Heroes_of_Hiis/Assets/Rotary Heart/SerializableDictionaryLite/Editor/DictionaryPropertyDrawer.cs

1028 lines
40 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace RotaryHeart.Lib.SerializableDictionary
{
[CustomPropertyDrawer(typeof(DrawableDictionary), true)]
public class DictionaryPropertyDrawer : PropertyDrawer
{
#region Fields
SerializedProperty reqReferences;
SerializedProperty isExpanded;
SerializedProperty KeysValues;
SerializedProperty KeysProp;
SerializedProperty ValuesProp;
readonly GUIContent idContent = new GUIContent("Id");
readonly GUIContent valueContent = new GUIContent("Value");
readonly GUIStyle tooTipStyle = new GUIStyle("Tooltip");
ReorderableList list;
string title;
System.Type[] typesNative =
{
typeof(bool),
typeof(byte),
typeof(float),
typeof(int),
typeof(string),
typeof(Vector2),
typeof(Vector3),
typeof(Vector4),
typeof(Quaternion),
typeof(Matrix4x4),
typeof(Color),
typeof(Rect),
typeof(LayerMask)
};
#endregion
/// <summary>
/// Used to get the required references, returns the reference to the ReorderableList
/// </summary>
/// <param name="property">This property</param>
private SerializedProperty GetReferences(SerializedProperty property)
{
SerializedProperty listProp = property.FindPropertyRelative("reorderableList");
reqReferences = property.FindPropertyRelative("reqReferences");
isExpanded = property.FindPropertyRelative("isExpanded");
list = GetTargetObjectOfProperty(listProp) as ReorderableList;
KeysValues = property.FindPropertyRelative("_keyValues");
KeysProp = property.FindPropertyRelative("_keys");
ValuesProp = property.FindPropertyRelative("_values");
return listProp;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
GetReferences(property);
if (list == null)
return 0;
//Default header height
float height = EditorGUIUtility.singleLineHeight;
//Default space between entires
float verticalSpace = list.verticalSpacing * 2;
//Add another line if it has the req reference drawn
if (KeysProp.arrayElementType.Contains("$"))
height += EditorGUIUtility.singleLineHeight;
if (isExpanded.boolValue)
{
//Default height for the bottom section
height += EditorGUIUtility.singleLineHeight;
int keysSize = KeysProp.arraySize;
if (Constants.ShowPages)
{
//Extra space for top section pages
height += EditorGUIUtility.singleLineHeight;
keysSize = Mathf.Min(keysSize, Constants.PageCount);
}
if (keysSize > 0)
{
//Iterate through all the keys
for (int keyIndex = 0; keyIndex < keysSize; keyIndex++)
{
//Should only happen with pages
if (keyIndex >= KeysProp.arraySize)
break;
var keyProp = KeysProp.GetArrayElementAtIndex(keyIndex);
//Use the same element height calculations, adding 4 because of the
height += List_getElementHeightCallback(keyProp, keyIndex) + verticalSpace;
}
}
else
//Default height for empty list
height += EditorGUIUtility.singleLineHeight + verticalSpace * 3;
}
return height + verticalSpace;
}
#region Helpers
private object GetTargetObjectOfProperty(SerializedProperty prop)
{
var path = prop.propertyPath.Replace(".Array.data[", "[");
object obj = prop.serializedObject.targetObject;
var elements = path.Split('.');
foreach (var element in elements)
{
if (element.Contains("["))
{
var elementName = element.Substring(0, element.IndexOf("["));
var index = System.Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
obj = GetValue_Imp(obj, elementName, index);
}
else
{
obj = GetValue_Imp(obj, element);
}
}
return obj;
}
private void SetTargetObjectOfProperty(SerializedProperty prop, object value, bool custom = false)
{
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("["));
var index = System.Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
obj = GetValue_Imp(obj, elementName, index);
}
else
{
obj = GetValue_Imp(obj, element);
}
}
if (Object.ReferenceEquals(obj, null)) return;
try
{
var element = elements.Last();
var tp = obj.GetType();
if (custom)
tp = tp.BaseType;
if (element.Contains("["))
{
var elementName = element.Substring(0, element.IndexOf("["));
var index = System.Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
var field = tp.GetField(elementName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var arr = field.GetValue(obj) as System.Collections.IList;
arr[index] = value;
}
else
{
var field = tp.GetField(element, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
field.SetValue(obj, value);
}
}
}
catch
{
return;
}
}
private object GetValue_Imp(object source, string name)
{
if (source == null)
return null;
var type = source.GetType();
while (type != null)
{
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);
type = type.BaseType;
}
return null;
}
private object GetValue_Imp(object source, string name, int index)
{
var enumerable = GetValue_Imp(source, name) as System.Collections.IEnumerable;
if (enumerable == null) return null;
var enm = enumerable.GetEnumerator();
//while (index-- >= 0)
// enm.MoveNext();
//return enm.Current;
for (int i = 0; i <= index; i++)
{
if (!enm.MoveNext()) return null;
}
return enm.Current;
}
private bool IsUnitySerialized(FieldInfo fieldInfo)
{
object[] customAttributes = fieldInfo.GetCustomAttributes(true);
if (customAttributes.Any(x => x is System.NonSerializedAttribute))
{
return false;
}
if (fieldInfo.IsPrivate && !customAttributes.Any(x => x is SerializeField))
{
return false;
}
return IsUnitySerialized(fieldInfo.FieldType);
}
private bool IsUnitySerialized(System.Type type)
{
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(List<>))
{
return IsUnitySerialized(type.GetGenericArguments()[0]);
}
return false;
}
if (type.IsEnum)
{
return true;
}
if (type.IsValueType)
{
return true;
}
if (type.IsAssignableFrom(typeof(Object)))
{
return true;
}
if (typesNative.Contains(type) || (type.IsArray && typesNative.Contains(type.GetElementType())))
{
return true;
}
return false;
}
/// <summary>
/// Converts a Vector4 to Quaternion
/// </summary>
/// <param name="v4">Vector to convert</param>
private Quaternion ConvertToQuaternion(Vector4 v4)
{
return new Quaternion(v4.x, v4.y, v4.z, v4.w);
}
/// <summary>
/// Converts a Quaternion to Vector4
/// </summary>
/// <param name="q">Quaternion to convert</param>
private Vector4 QuaternionToVector4(Quaternion q)
{
return new Vector4(q.x, q.y, q.z, q.w);
}
#endregion
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty listProp = GetReferences(property);
reqReferences.isExpanded = false;
property.isExpanded = false;
title = label.text;
Rect nextRect;
string keyType = KeysProp.arrayElementType;
int offset = 0;
//Only draw the required references field for keys that requires a default value
if (keyType.Contains("$"))
{
nextRect = GetNextRect(ref position);
EditorGUI.PropertyField(nextRect, reqReferences);
offset = 20;
}
nextRect = GetNextRect(ref position);
//Fix values size based on the keys size
if (ValuesProp.arraySize != KeysProp.arraySize)
ValuesProp.arraySize = KeysProp.arraySize;
if (KeysValues.arraySize != KeysProp.arraySize)
KeysValues.arraySize = KeysProp.arraySize;
if (list != null)
{
if (!list.HasList)
{
list = new ReorderableList(KeysProp, true, true, true);
//Required callbacks
list.onRemoveCallback += List_onRemoveCallback;
list.onAddCallback += List_onAddCallback;
list.drawElementCallback += List_drawElementCallback;
list.drawHeaderCallback += List_drawHeaderCallback;
list.getElementHeightCallback += List_getElementHeightCallback;
list.onElementsReorder += List_onElementsReorder;
list.headerExpand += List_headerExpand;
SetTargetObjectOfProperty(listProp, list);
}
list.List = KeysProp;
list.isExpanded = isExpanded.boolValue;
list.DoList(new Rect(nextRect.x, nextRect.y, nextRect.width, GetPropertyHeight(property, label) - offset), label, Constants.ShowPages, Constants.PageCount);
}
}
private void List_headerExpand(bool expand)
{
isExpanded.boolValue = expand;
for (int i = 0; i < KeysValues.arraySize; i++)
{
KeysProp.GetArrayElementAtIndex(i).isExpanded = expand;
ValuesProp.GetArrayElementAtIndex(i).isExpanded = expand;
}
}
private void List_onElementsReorder(int startIndex, int newIndex)
{
KeysValues.MoveArrayElement(startIndex, newIndex);
//KeysProp.MoveArrayElement(startIndex, newIndex);
ValuesProp.MoveArrayElement(startIndex, newIndex);
}
private void List_drawHeaderCallback(Rect rect, GUIContent label)
{
rect.x += 6;
isExpanded.boolValue = EditorGUI.Foldout(rect, isExpanded.boolValue, "", true);
isExpanded.serializedObject.ApplyModifiedProperties();
EditorGUI.LabelField(rect, title + (Constants.ShowSize ? " [" + KeysValues.arraySize + "]" : ""));
}
private float List_getElementHeightCallback(SerializedProperty element, int index)
{
float height;
bool containsAttribute = fieldInfo.GetCustomAttributes(typeof(DrawKeyAsPropertyAttribute), true).Any();
height = EditorGUI.GetPropertyHeight(element, GUIContent.none, true) + list.verticalSpacing * 4;
//Value height
if (element.isExpanded)
{
var valueProp = ValuesProp.GetArrayElementAtIndex(index);
height += EditorGUI.GetPropertyHeight(valueProp, GUIContent.none, true) + list.verticalSpacing - (containsAttribute ? EditorGUIUtility.singleLineHeight : 0);
}
return height;
}
private void List_drawElementCallback(Rect rect, SerializedProperty element, GUIContent label, int index, bool selected, bool focused)
{
var keyValueProp = KeysValues.GetArrayElementAtIndex(index);
var keyProp = KeysProp.GetArrayElementAtIndex(index);
var valueProp = ValuesProp.GetArrayElementAtIndex(index);
SerializedProperty keyToUse = keyProp.propertyType == SerializedPropertyType.Generic ? keyProp : keyValueProp;
if (keyToUse.propertyType == SerializedPropertyType.Generic)
{
rect.x -= 10;
rect.width += 10;
}
//Only draw the color if this entry is not selected
if (!selected)
{
if (Event.current.type == EventType.Repaint)
tooTipStyle.Draw(rect, false, false, false, false);
}
rect.height = EditorGUIUtility.singleLineHeight;
//Check if it contains the new attribute
bool containsAttribute = fieldInfo.GetCustomAttributes(typeof(DrawKeyAsPropertyAttribute), true).Any();
Rect keyRect = new Rect(rect.x + 50, rect.y + 4, rect.width - 52, rect.height);
Rect valueRect = new Rect(keyRect);
#region Key Field
string propName = "";
if (containsAttribute)
{
var field = fieldInfo.FieldType.BaseType.GetField("_keys", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
var fieldType = field.FieldType;
var elementType = fieldType.GetGenericArguments()[0];
foreach (var fi in elementType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
{
if (IsUnitySerialized(fi))
{
propName = fi.Name;
break;
}
}
}
//Draw only if its not a generic type or can be draw as property
if ((containsAttribute && !string.IsNullOrEmpty(propName)) || keyToUse.propertyType != SerializedPropertyType.Generic)
{
if (containsAttribute)
keyRect.height = EditorGUI.GetPropertyHeight(keyProp, GUIContent.none, true) - (keyProp.isExpanded ? EditorGUIUtility.singleLineHeight : 0);
keyProp.isExpanded = EditorGUI.Foldout(new Rect(rect.x + 15, keyRect.y, 20, rect.height), keyProp.isExpanded, idContent, true);
}
GUI.SetNextControlName("CheckGenericFocus" + index);
switch (keyToUse.propertyType)
{
case SerializedPropertyType.Quaternion:
EditorGUI.BeginChangeCheck();
var newV4 = EditorGUI.Vector4Field(keyRect, GUIContent.none, QuaternionToVector4(keyToUse.quaternionValue));
if (EditorGUI.EndChangeCheck())
{
keyToUse.quaternionValue = ConvertToQuaternion(newV4);
}
break;
case SerializedPropertyType.Enum:
string[] names = keyToUse.enumDisplayNames;
if (names.Length <= keyToUse.enumValueIndex || keyToUse.enumValueIndex < 0)
{
list.Selected = new[] {index};
List_onRemoveCallback(list);
return;
}
var selectedVal = names[keyToUse.enumValueIndex];
//Draw button with dropdown style
if (GUI.Button(keyRect, selectedVal, EditorStyles.layerMaskField))
{
List<string> usedNames = new List<string>();
GenericMenu menu = new GenericMenu();
//Add all the used values
for (int i = 0; i < KeysValues.arraySize; i++)
{
usedNames.Add(names[KeysValues.GetArrayElementAtIndex(i).enumValueIndex]);
}
//Add all the menu items
for (int i = 0; i < names.Length; i++)
{
int nameIndex = i;
//If the value is being used, show it disabled
if (usedNames.Contains(names[nameIndex]) && !names[nameIndex].Equals(selectedVal))
{
menu.AddDisabledItem(new GUIContent(names[nameIndex]));
}
else
{
menu.AddItem(new GUIContent(names[nameIndex]), selectedVal == names[nameIndex], () =>
{
keyValueProp.enumValueIndex = nameIndex;
keyProp.enumValueIndex = nameIndex;
keyToUse.serializedObject.ApplyModifiedProperties();
});
}
}
//Show menu under mouse position
menu.ShowAsContext();
Event.current.Use();
}
break;
case SerializedPropertyType.Generic:
//Only draw as property if values are correct
if (containsAttribute && !string.IsNullOrEmpty(propName))
{
EditorGUI.PropertyField(keyRect, keyToUse.FindPropertyRelative(propName), GUIContent.none, false);
}
else
{
keyRect.height = EditorGUI.GetPropertyHeight(keyToUse, idContent);
EditorGUI.PropertyField(new Rect(rect.x + 15, keyRect.y, keyRect.width + 35, keyRect.height), keyToUse, idContent, true);
}
break;
default:
EditorGUI.PropertyField(keyRect, keyToUse, GUIContent.none, false);
break;
}
//Not used for generic type
if (keyToUse.propertyType != SerializedPropertyType.Generic)
{
//Old key value
var oldId = GetKeyValue(keyProp);
//New key value
var newId = GetKeyValue(keyValueProp);
//Notify if the key is empty or null
if ((keyToUse.propertyType == SerializedPropertyType.String && string.IsNullOrEmpty(newId.ToString())) || newId == null)
{
GUIContent content = EditorGUIUtility.IconContent("console.warnicon.sml");
content.tooltip = "ID cannot be left empty";
GUI.Button(new Rect(keyRect.x - 15, keyRect.y, 30, 30), content, GUIStyle.none);
}
//Check if the key value has been changed
else if ((oldId == null && newId != null) || !oldId.Equals(newId))
{
//Be sure that the dictionary doesn't contain an element with this key
if (ContainsId(newId, index))
{
//Check if this key is still focused
if (GUI.GetNameOfFocusedControl().Equals("CheckGenericFocus" + index))
{
//Notify the user that this key already exists
GUIContent content = EditorGUIUtility.IconContent("console.erroricon.sml");
content.tooltip = "Dictionary already has this id, this id cannot be used";
GUI.Button(new Rect(keyRect.x - 15, keyRect.y, 30, 30), content, GUIStyle.none);
}
else
{
//If it's not, set the correct key back. This is to avoid having multiple errors with ids
SetValue(keyValueProp, oldId);
}
}
else
{
//Set the value
SetGenericValue(keyProp, valueProp, newId);
}
}
}
#endregion Fey Field
valueRect.y = keyRect.yMax + 3 - (containsAttribute ? 2 : 0);
valueRect.x -= 20;
valueRect.width += 20;
#region Value Field
//Special case for generic types
if (valueProp.propertyType == SerializedPropertyType.Generic)
{
if (keyToUse.propertyType != SerializedPropertyType.Generic)
valueRect.y -= 3;
EditorGUI.BeginChangeCheck();
//Value field
if (keyProp.isExpanded)
{
EditorGUI.BeginProperty(valueRect, GUIContent.none, valueProp);
if (valueProp.propertyType == SerializedPropertyType.Quaternion)
{
EditorGUI.BeginChangeCheck();
var newV4 = EditorGUI.Vector4Field(valueRect, GUIContent.none, QuaternionToVector4(valueProp.quaternionValue));
if (EditorGUI.EndChangeCheck())
{
valueProp.quaternionValue = ConvertToQuaternion(newV4);
}
}
else
{
EditorGUI.PropertyField(valueRect, valueProp, valueContent, true);
}
EditorGUI.EndProperty();
}
if (EditorGUI.EndChangeCheck())
{
//This is used to apply the modified changes
ValuesProp.serializedObject.ApplyModifiedProperties();
}
}
else
{
//Value field
if (keyProp.isExpanded)
{
valueRect.x -= 10;
valueRect.width += 10;
EditorGUI.BeginProperty(valueRect, GUIContent.none, valueProp);
EditorGUI.PrefixLabel(valueRect, valueContent);
if (valueProp.propertyType == SerializedPropertyType.Quaternion)
{
EditorGUI.BeginChangeCheck();
var newV4 = EditorGUI.Vector4Field(new Rect(valueRect.x + 45, valueRect.y, valueRect.width - 45, valueRect.height), GUIContent.none, QuaternionToVector4(valueProp.quaternionValue));
if (EditorGUI.EndChangeCheck())
{
valueProp.quaternionValue = ConvertToQuaternion(newV4);
}
}
else
{
EditorGUI.PropertyField(new Rect(valueRect.x + 45, valueRect.y, valueRect.width - 45, valueRect.height), valueProp, GUIContent.none, true);
}
EditorGUI.EndProperty();
}
}
#endregion Value Field
}
private void List_onAddCallback(ReorderableList list)
{
KeysValues.arraySize = ValuesProp.arraySize = ++KeysProp.arraySize;
SetPropertyDefault(KeysValues.GetArrayElementAtIndex(KeysValues.arraySize - 1), KeysValues);
SetPropertyDefault(KeysProp.GetArrayElementAtIndex(KeysProp.arraySize - 1), KeysProp);
KeysValues.serializedObject.ApplyModifiedProperties();
ValuesProp.serializedObject.ApplyModifiedProperties();
KeysProp.serializedObject.ApplyModifiedProperties();
//SetPropertyDefault(ValuesProp.GetArrayElementAtIndex(ValuesProp.arraySize - 1), null);
}
private void List_onRemoveCallback(ReorderableList list)
{
for (int i = list.Selected.Length - 1; i >= 0; i--)
{
int index = list.Selected[i];
int last = KeysProp.arraySize - 1;
KeysValues.MoveArrayElement(index, last);
KeysProp.MoveArrayElement(index, last);
ValuesProp.MoveArrayElement(index, last);
KeysValues.arraySize--;
KeysProp.arraySize--;
ValuesProp.arraySize--;
}
ValuesProp.serializedObject.ApplyModifiedProperties();
ValuesProp.serializedObject.Update();
}
/// <summary>
/// Checks if the <paramref name="KeysProp"/> contains the id
/// </summary>
/// <param name="obj">Id to check</param>
/// <param name="index">Property index on the array</param>
/// <returns>True if an element is already using the id; otherwise, false</returns>
private bool ContainsId(object obj, int index)
{
for (int i = 0; i < KeysProp.arraySize; i++)
{
if (index == i)
{
continue;
}
object val = GetKeyValue(KeysProp.GetArrayElementAtIndex(i));
if (val.Equals(obj))
return true;
}
return false;
}
/// <summary>
/// Returns the current property value
/// </summary>
/// <param name="prop">Property to check</param>
/// <returns>object representation of the property value</returns>
private object GetKeyValue(SerializedProperty prop)
{
object obj = null;
switch (prop.propertyType)
{
case SerializedPropertyType.Integer:
case SerializedPropertyType.LayerMask:
obj = prop.intValue;
break;
case SerializedPropertyType.Boolean:
obj = prop.boolValue;
break;
case SerializedPropertyType.Float:
obj = prop.floatValue;
break;
case SerializedPropertyType.String:
obj = prop.stringValue;
break;
case SerializedPropertyType.Color:
obj = prop.colorValue;
break;
case SerializedPropertyType.ObjectReference:
obj = prop.objectReferenceValue;
break;
case SerializedPropertyType.Enum:
obj = prop.enumValueIndex;
break;
case SerializedPropertyType.Vector2:
obj = prop.vector2Value;
break;
case SerializedPropertyType.Vector3:
obj = prop.vector3Value;
break;
case SerializedPropertyType.Vector4:
obj = prop.vector4Value;
break;
case SerializedPropertyType.Rect:
obj = prop.rectValue;
break;
case SerializedPropertyType.ArraySize:
obj = prop.arraySize;
break;
case SerializedPropertyType.Character:
obj = (char)prop.intValue;
break;
case SerializedPropertyType.AnimationCurve:
obj = prop.animationCurveValue;
break;
case SerializedPropertyType.Bounds:
obj = prop.boundsValue;
break;
case SerializedPropertyType.Gradient:
obj = GetGradientValue(prop);
break;
case SerializedPropertyType.Quaternion:
obj = prop.quaternionValue;
break;
case SerializedPropertyType.Generic:
obj = GetTargetObjectOfProperty(prop);
break;
default:
Debug.LogError("Key Type not implemented: " + prop.propertyType);
break;
}
return obj;
}
/// <summary>
/// Sets the property value
/// </summary>
/// <param name="prop">Property to modify</param>
/// <param name="obj">Value</param>
private void SetValue(SerializedProperty prop, object obj)
{
switch (prop.propertyType)
{
case SerializedPropertyType.Integer:
case SerializedPropertyType.LayerMask:
prop.intValue = (int)obj;
break;
case SerializedPropertyType.Boolean:
prop.boolValue = (bool)obj;
break;
case SerializedPropertyType.Float:
prop.floatValue = (float)obj;
break;
case SerializedPropertyType.String:
prop.stringValue = (string)obj;
break;
case SerializedPropertyType.Color:
prop.colorValue = (Color)obj;
break;
case SerializedPropertyType.ObjectReference:
prop.objectReferenceValue = (Object)obj;
break;
case SerializedPropertyType.Enum:
prop.enumValueIndex = (int)obj;
break;
case SerializedPropertyType.Vector2:
prop.vector2Value = (Vector2)obj;
break;
case SerializedPropertyType.Vector3:
prop.vector3Value = (Vector3)obj;
break;
case SerializedPropertyType.Vector4:
prop.vector4Value = (Vector4)obj;
break;
case SerializedPropertyType.Rect:
prop.rectValue = (Rect)obj;
break;
case SerializedPropertyType.ArraySize:
prop.arraySize = (int)obj;
break;
case SerializedPropertyType.Character:
prop.intValue = (char)obj;
break;
case SerializedPropertyType.AnimationCurve:
prop.animationCurveValue = (AnimationCurve)obj;
break;
case SerializedPropertyType.Bounds:
prop.boundsValue = (Bounds)obj;
break;
case SerializedPropertyType.Gradient:
SetGradientValue(prop, (Gradient)obj);
break;
case SerializedPropertyType.Quaternion:
prop.quaternionValue = (Quaternion)obj;
break;
case SerializedPropertyType.Generic:
SetTargetObjectOfProperty(prop, null);
break;
default:
Debug.Log("Type not implemented: " + prop.propertyType);
break;
}
}
/// <summary>
/// Tries to get the Gradient value of the <paramref name="prop"/> using reflection, if it fails returns null
/// </summary>
/// <param name="prop">SerializedProperty to get the value from</param>
/// <returns>Gradient value, or null if it fails</returns>
private Gradient GetGradientValue(SerializedProperty prop)
{
PropertyInfo propertyInfo = typeof(SerializedProperty).GetProperty("gradientValue", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (propertyInfo == null)
return null;
return propertyInfo.GetValue(prop, null) as Gradient;
}
/// <summary>
/// Tries to set the Gradient value of the <paramref name="prop"/> using reflection, if it fails nothing is saved
/// </summary>
/// <param name="prop">SerializedProperty to get the value from</param>
/// <param name="gradient">Gradient value to save</param>
private void SetGradientValue(SerializedProperty prop, Gradient gradient)
{
PropertyInfo propertyInfo = typeof(SerializedProperty).GetProperty("gradientValue", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (propertyInfo == null)
return;
propertyInfo.SetValue(prop, gradient, null);
}
/// <summary>
/// Special check for a dictionary with a generic value type. This tries to set the key to its id field
/// </summary>
/// <param name="keyProp">Key proeprty</param>
/// <param name="valueProp">Value property</param>
/// <param name="obj">Key value to set</param>
private void SetGenericValue(SerializedProperty keyProp, SerializedProperty valueProp, object obj)
{
SetValue(keyProp, obj);
IDAttribute attribute = System.Attribute.GetCustomAttribute(fieldInfo, typeof(IDAttribute)) as IDAttribute;
if (attribute == null)
{
//This generic dictionary doesn't contain an id attribute
return;
}
SerializedProperty id = valueProp.FindPropertyRelative(attribute.Id);
if (id == null)
{
Debug.LogError("Couldn't find any id field with name '" + attribute.Id + "' on field: " + fieldInfo.Name);
return;
}
SetValue(id, obj);
}
/// <summary>
/// Returns the next rect forcing the height to be of a single line
/// </summary>
/// <param name="position">Position reference</param>
/// <returns>Next rect</returns>
private Rect GetNextRect(ref Rect position)
{
var h = EditorGUIUtility.singleLineHeight;
var r = new Rect(position.x, position.y, position.width, h);
position = new Rect(position.x, position.y + h, position.width, h);
return r;
}
/// <summary>
/// Sets the default value based on the property
/// </summary>
/// <param name="prop">Property</param>
private void SetPropertyDefault(SerializedProperty prop, SerializedProperty parentProperty)
{
if (prop == null)
throw new System.ArgumentNullException("prop");
switch (prop.propertyType)
{
case SerializedPropertyType.Integer:
prop.intValue = int.MaxValue;
break;
case SerializedPropertyType.Boolean:
prop.boolValue = false;
break;
case SerializedPropertyType.Float:
prop.floatValue = Mathf.Infinity;
break;
case SerializedPropertyType.String:
prop.stringValue = string.Empty;
break;
case SerializedPropertyType.Color:
prop.colorValue = Color.black;
break;
case SerializedPropertyType.ObjectReference:
prop.objectReferenceValue = null;
break;
case SerializedPropertyType.LayerMask:
prop.intValue = -1;
break;
case SerializedPropertyType.Enum:
int index = 0;
if (parentProperty != null)
{
List<int> numbersUsed = new List<int>();
for (int i = 0; i < parentProperty.arraySize; i++)
numbersUsed.Add(parentProperty.GetArrayElementAtIndex(i).enumValueIndex);
while (true)
{
if (!numbersUsed.Contains(index))
{
break;
}
index++;
}
if (index >= prop.enumNames.Length)
index = 0;
}
prop.enumValueIndex = index;
break;
case SerializedPropertyType.Vector2:
prop.vector2Value = Vector2.zero;
break;
case SerializedPropertyType.Vector3:
prop.vector3Value = Vector3.zero;
break;
case SerializedPropertyType.Vector4:
prop.vector4Value = Vector4.zero;
break;
case SerializedPropertyType.Rect:
prop.rectValue = Rect.zero;
break;
case SerializedPropertyType.ArraySize:
prop.arraySize = 0;
break;
case SerializedPropertyType.Character:
prop.intValue = 0;
break;
case SerializedPropertyType.AnimationCurve:
prop.animationCurveValue = null;
break;
case SerializedPropertyType.Bounds:
prop.boundsValue = default(Bounds);
break;
case SerializedPropertyType.Gradient:
SetGradientValue(prop, new Gradient());
break;
case SerializedPropertyType.Generic:
//Used to initialized all the values on the generic type
var t = prop.GetEnumerator();
while (t.MoveNext())
{
var val = t.Current;
SetPropertyDefault((val as SerializedProperty), null);
}
break;
case SerializedPropertyType.Quaternion:
prop.quaternionValue = Quaternion.identity;
break;
default:
Debug.Log("Type not implemented: " + prop.propertyType);
break;
}
}
}
}