2022-09-06 13:47:45 +08:00

1257 lines
50 KiB
C#

// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
#if UNITY_5_4 || UNITY_5_3 || UNITY_5_2 || UNITY_5_1 || UNITY_5_0
#define UNITY_PRE_5_5
#endif
using System;
using UnityEngine;
using UnityEditor;
using FluffyUnderware.DevTools;
using FluffyUnderware.DevTools.Extensions;
using FluffyUnderware.DevToolsEditor.Extensions;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using MinAttribute = FluffyUnderware.DevTools.MinAttribute;
namespace FluffyUnderware.DevToolsEditor
{
public class DTPropertyDrawer<T> : PropertyDrawer where T : DTPropertyAttribute
{
/// <summary>
/// Gets the attribute
/// </summary>
protected T Attribute
{
get { return attribute as T; }
}
/// <summary>
/// Gets some common attribute options
/// </summary>
protected AttributeOptionsFlags Options
{
get { return Attribute.Options; }
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (!string.IsNullOrEmpty(Attribute.Label))
label.text = Attribute.Label;
label.tooltip = Attribute.Tooltip;
}
protected void PushCustomColors()
{
if (!string.IsNullOrEmpty(Attribute.Color))
DTGUI.PushColor(Attribute.Color.ColorFromHtml());
}
protected void PopCustomColors()
{
if (!string.IsNullOrEmpty(Attribute.Color))
DTGUI.PopColor();
}
protected MemberInfo GetMemberInfo(SerializedProperty property, string fieldOrPropertyName, out object parent)
{
parent = property.GetParentObject();
Type parentType = parent.GetType();
FieldInfo fi = parentType.FieldByName(fieldOrPropertyName, true, true);
if (fi != null)
return fi;
else
{
PropertyInfo pi = parentType.PropertyByName(fieldOrPropertyName, true, true);
if (pi != null)
return pi;
else
{
DTLog.LogError(parentType.Name + ": Field or Property '" + fieldOrPropertyName + "' not found!");
return null;
}
}
}
/// <summary>
/// Get a value from a field or property of the serialized object by it's name
/// </summary>
/// <typeparam name="U">datatype to retrieve</typeparam>
/// <param name="property">a serialized property from the object in question</param>
/// <param name="fieldOrPropertyName">name of the field or property</param>
/// <returns>the value of the field or property</returns>
protected U GetMemberValue<U>(SerializedProperty property, string fieldOrPropertyName)
{
object parent;
object o = null;
MemberInfo mi = GetMemberInfo(property, fieldOrPropertyName, out parent);
if (mi != null)
{
if (mi is FieldInfo)
o = ((FieldInfo)mi).GetValue(parent);
else
o = ((PropertyInfo)mi).GetValue(parent, null);
}
Type oT = o.GetType();
Type rT = typeof(U);
if (oT == rT)
return (U)o;
else if (oT == typeof(int) && rT == typeof(float))
return (U)System.Convert.ChangeType(o, rT, System.Globalization.CultureInfo.InvariantCulture);
else
return default(U);
}
protected object GetParent(SerializedProperty prop)
{
string path = prop.propertyPath.Replace(".Array.data[", "[");
object obj = prop.serializedObject.targetObject;
string[] elements = path.Split('.');
foreach (string element in elements.Take(elements.Length - 1))
{
if (element.Contains("["))
{
string elementName = element.Substring(0, element.IndexOf("["));
int index = System.Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
obj = GetValue(obj, elementName, index);
}
else
{
obj = GetValue(obj, element);
}
}
return obj;
}
object GetValue(object source, string name)
{
if (source == null)
return null;
Type type = source.GetType();
FieldInfo f = type.FieldByName(name, true, true);
if (f == null)
{
PropertyInfo p = type.PropertyByName(name, true, true);
if (p == null)
return null;
return p.GetValue(source, null);
}
return f.GetValue(source);
}
object GetValue(object source, string name, int index)
{
IEnumerable enumerable = GetValue(source, name) as IEnumerable;
IEnumerator enm = enumerable.GetEnumerator();
while (index-- >= 0)
enm.MoveNext();
return enm.Current;
}
protected U GetPropertySourceField<U>(SerializedProperty property)
{
return (U)fieldInfo.GetValue(GetParent(property));
/*
var instance = property.serializedObject.targetObject;
var t = instance.GetType();
FieldInfo fi = null;
while (fi == null && t != null)
{
fi = t.GetField(property.propertyPath, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fi == null)
t = t.BaseType;
}
return (fi != null) ? (U)fi.GetValue(instance) : default(U);
*/
}
}
[CustomPropertyDrawer(typeof(LabelAttribute))]
public class LabelAttributePropertyDrawer : DTPropertyDrawer<LabelAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
EditorGUI.PropertyField(position, property, label);
}
}
[CustomPropertyDrawer(typeof(ToggleButtonAttribute))]
public class ToggleButtonAttributePropertyDrawer : DTPropertyDrawer<ToggleButtonAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
if (property.propertyType != SerializedPropertyType.Boolean)
DTLog.LogError("[DevTools] - [ToggleButton] only valid for boolean fields!");
else
{
EditorGUI.BeginChangeCheck();
GUIStyle st = GUI.skin.button;
bool v = GUI.Toggle(position, property.boolValue, label, st);
if (EditorGUI.EndChangeCheck())
property.boolValue = v;
}
}
}
[CustomPropertyDrawer(typeof(LayerAttribute))]
public class LayerPropertyDrawer : DTPropertyDrawer<LayerAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, GUIContent.none, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
property.intValue = EditorGUI.LayerField(position, property.intValue);
EditorGUI.EndProperty();
}
}
[CustomPropertyDrawer(typeof(TagAttribute))]
public class TagPropertyDrawer : DTPropertyDrawer<TagAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, GUIContent.none, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
property.stringValue = EditorGUI.TagField(position, property.stringValue);
EditorGUI.EndProperty();
}
}
[CustomPropertyDrawer(typeof(VectorExAttribute))]
public class VectorExPropertyDrawer : DTPropertyDrawer<VectorExAttribute>
{
private static readonly GUIContent CopyButtonGuiContent = new GUIContent("C", "Copy");
private static readonly GUIContent PasteButtonGuiContent = new GUIContent("P", "Past");
private static readonly GUIContent SetZeroButtonGuiContent = new GUIContent("0", "Set to zero");
private static readonly GUIContent SetOneButtonGuiContent = new GUIContent("1", "Set to one");
private static readonly GUIContent NegateButtonGuiContent = new GUIContent("~", "Negate");
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float h;
if (Options.HasFlag(AttributeOptionsFlags.Compact) || EditorGUIUtility.wideMode)
h = base.GetPropertyHeight(property, label);
else
h = base.GetPropertyHeight(property, label) * 2;
return h;
}
public static void DoGUI(Rect position, SerializedProperty property, DTPropertyAttribute attribute, GUIContent label, AttributeOptionsFlags flags, VectorExPropertyDrawer drawer = null)
{
if (property.propertyType != SerializedPropertyType.Vector2 && property.propertyType != SerializedPropertyType.Vector3 && property.propertyType != SerializedPropertyType.Quaternion)
DTLog.LogError("[DevTools] - [VectorEx] only valid for Vector and Quaternion fields!");
else
{
int buttons = 0;
if (flags.HasFlag(AttributeOptionsFlags.Clipboard))
buttons += 2;
if (flags.HasFlag(AttributeOptionsFlags.One))
buttons++;
if (flags.HasFlag(AttributeOptionsFlags.Zero))
buttons++;
if (flags.HasFlag(AttributeOptionsFlags.Negate))
buttons++;
int buttonWidth = 21;
int interButtonSpace = 1;
int buttonsWidth = buttons * buttonWidth + Math.Max(0, buttons - 1) * interButtonSpace;
Rect buttonsRectangle = new Rect();
buttonsRectangle.x = position.x + position.width - buttonsWidth;
buttonsRectangle.y = position.y;
buttonsRectangle.width = buttonWidth;
buttonsRectangle.height = EditorGUIUtility.singleLineHeight;
Rect r = position.WithoutLabel();
r.width -= buttonsWidth;
if (flags.HasFlag(AttributeOptionsFlags.Compact) || EditorGUIUtility.wideMode)
{
if (drawer != null)
drawer.PushCustomColors();
EditorGUI.PrefixLabel(position, label);
if (property.propertyType == SerializedPropertyType.Vector2)
{
EditorGUI.BeginChangeCheck();
Vector2 v = DTGUI.CompactVector2Field(r, property.vector2Value);
if (EditorGUI.EndChangeCheck())
property.vector2Value = DT._UseSnapValuePrecision ? DTMath.SnapPrecision(v, attribute.Precision) : v;
}
else if (property.propertyType == SerializedPropertyType.Vector3)
{
EditorGUI.BeginChangeCheck();
Vector3 v = DTGUI.CompactVector3Field(r, property.vector3Value);
if (EditorGUI.EndChangeCheck())
property.vector3Value = DT._UseSnapValuePrecision ? DTMath.SnapPrecision(v, attribute.Precision) : v;
}
else if (property.propertyType == SerializedPropertyType.Quaternion)
{
EditorGUI.BeginChangeCheck();
Vector3 v = DTGUI.CompactVector3Field(r, DT._UseSnapValuePrecision ? DTMath.SnapPrecision(property.quaternionValue.eulerAngles, attribute.Precision) : property.quaternionValue.eulerAngles);
if (EditorGUI.EndChangeCheck())
property.quaternionValue = Quaternion.Euler(v);
}
if (drawer != null)
drawer.PopCustomColors();
}
else
{
EditorGUI.BeginChangeCheck();
if (drawer != null)
drawer.PushCustomColors();
if (DTEditorUtility.UsesNewEditorUI)
#pragma warning disable 162
position.y -= 2;
#pragma warning restore 162
EditorGUI.PropertyField(position, property, label);
if (drawer != null)
drawer.PopCustomColors();
if (EditorGUI.EndChangeCheck() && DT._UseSnapValuePrecision)
{
if (property.propertyType == SerializedPropertyType.Vector2)
property.vector2Value = DTMath.SnapPrecision(property.vector2Value, attribute.Precision);
else if (property.propertyType == SerializedPropertyType.Vector3)
property.vector3Value = DTMath.SnapPrecision(property.vector3Value, attribute.Precision);
}
}
if (flags.HasFlag(AttributeOptionsFlags.Clipboard))
{
if (GUI.Button(buttonsRectangle, CopyButtonGuiContent, EditorStyles.miniButton))
if (property.propertyType == SerializedPropertyType.Vector2)
DT.ClipboardCopy(property.vector2Value);
else if (property.propertyType == SerializedPropertyType.Vector3)
DT.ClipboardCopy(property.vector3Value);
buttonsRectangle.x += buttonWidth + interButtonSpace;
// Can Paste?
if (property.propertyType == SerializedPropertyType.Vector2)
GUI.enabled = DT.ClipboardCanPasteTo<Vector2>();
else if (property.propertyType == SerializedPropertyType.Vector3)
GUI.enabled = DT.ClipboardCanPasteTo<Vector3>();
if (GUI.Button(buttonsRectangle, PasteButtonGuiContent, EditorStyles.miniButton))
if (property.propertyType == SerializedPropertyType.Vector2)
property.vector2Value = DT.ClipboardPaste<Vector2>();
else if (property.propertyType == SerializedPropertyType.Vector3)
property.vector3Value = DT.ClipboardPaste<Vector3>();
GUI.enabled = true;
buttonsRectangle.x += buttonWidth + interButtonSpace;
}
if (flags.HasFlag(AttributeOptionsFlags.Zero))
{
if (GUI.Button(buttonsRectangle, SetZeroButtonGuiContent, EditorStyles.miniButton))
if (property.propertyType == SerializedPropertyType.Vector2)
property.vector2Value = Vector2.zero;
else if (property.propertyType == SerializedPropertyType.Vector3)
property.vector3Value = Vector3.zero;
buttonsRectangle.x += buttonWidth + interButtonSpace;
}
if (flags.HasFlag(AttributeOptionsFlags.One))
{
if (GUI.Button(buttonsRectangle, SetOneButtonGuiContent, EditorStyles.miniButton))
if (property.propertyType == SerializedPropertyType.Vector2)
property.vector2Value = Vector2.one;
else if (property.propertyType == SerializedPropertyType.Vector3)
property.vector3Value = Vector3.one;
buttonsRectangle.x += buttonWidth + interButtonSpace;
}
if (flags.HasFlag(AttributeOptionsFlags.Negate))
{
if (GUI.Button(buttonsRectangle, NegateButtonGuiContent, EditorStyles.miniButton))
if (property.propertyType == SerializedPropertyType.Vector2)
property.vector2Value *= -1;
else if (property.propertyType == SerializedPropertyType.Vector3)
property.vector3Value *= -1;
buttonsRectangle.x += buttonWidth + interButtonSpace;
}
}
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
DoGUI(position, property, Attribute, label, Options, this);
}
}
[CustomPropertyDrawer(typeof(AnimationCurveExAttribute))]
public class AnimationCurveExPropertyDrawer : DTPropertyDrawer<AnimationCurveExAttribute>
{
public override void OnGUI(Rect propertyRectangle, SerializedProperty property, GUIContent label)
{
base.OnGUI(propertyRectangle, property, label);
if (property.propertyType != SerializedPropertyType.AnimationCurve)
DTLog.LogError("[DevTools] - [AnimationCurvEx] only valid for AnimationCurve fields!");
else
{
int buttons = 0;
if (Options.HasFlag(AttributeOptionsFlags.Clipboard))
buttons += 2;
int buttonWidth = 21;
int interButtonSpace = 1;
int buttonsWidth = buttons * buttonWidth + Math.Max(0, buttons - 1) * interButtonSpace;
Rect buttonsRectangle = new Rect();
buttonsRectangle.x = propertyRectangle.x + propertyRectangle.width - buttonsWidth;
buttonsRectangle.y = propertyRectangle.y;
buttonsRectangle.width = buttonWidth;
buttonsRectangle.height = EditorGUIUtility.singleLineHeight;
propertyRectangle.width -= buttonsWidth;
EditorGUI.PropertyField(propertyRectangle, property, label);
// Buttons
if (Options.HasFlag(AttributeOptionsFlags.Clipboard))
{
if (GUI.Button(buttonsRectangle, "C", EditorStyles.miniButton))
DT.ClipboardCopy(property.animationCurveValue);
buttonsRectangle.x += buttonWidth + interButtonSpace;
GUI.enabled = DT.ClipboardCanPasteTo<AnimationCurve>();
if (GUI.Button(buttonsRectangle, "P", EditorStyles.miniButton))
property.animationCurveValue = DT.ClipboardPaste<AnimationCurve>();
GUI.enabled = true;
}
}
}
}
[CustomPropertyDrawer(typeof(RangeExAttribute))]
public class RangeExPropertyDrawer : DTPropertyDrawer<RangeExAttribute>
{
float minV;
float maxV;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
minV = Attribute.MinValue;
maxV = Attribute.MaxValue;
if (!string.IsNullOrEmpty(Attribute.MinFieldOrPropertyName))
{
minV = GetMemberValue<float>(property, Attribute.MinFieldOrPropertyName);
}
if (!string.IsNullOrEmpty(Attribute.MaxFieldOrPropertyName))
{
maxV = GetMemberValue<float>(property, Attribute.MaxFieldOrPropertyName);
}
if (minV > maxV)
{
float temp = minV;
minV = maxV;
maxV = temp;
}
// GUI
PushCustomColors();
EditorGUI.BeginChangeCheck();
if (Attribute.Slider)
{
switch (property.propertyType)
{
case SerializedPropertyType.Float:
EditorGUI.Slider(position, property, minV, maxV, label);
break;
case SerializedPropertyType.Integer:
EditorGUI.IntSlider(position, property, (int)minV, (int)maxV, label);
break;
}
}
else
EditorGUI.PropertyField(position, property, label);
if (EditorGUI.EndChangeCheck())
{
// Clamp
switch (property.propertyType)
{
case SerializedPropertyType.Float:
if (DT._UseSnapValuePrecision && Attribute.Precision > -1)
property.floatValue = DTMath.SnapPrecision(property.floatValue, Attribute.Precision);
if (property.floatValue < minV || property.floatValue > maxV)
property.floatValue = Mathf.Clamp(property.floatValue, minV, maxV);
break;
case SerializedPropertyType.Integer:
if (property.intValue < minV || property.intValue > maxV)
property.intValue = Mathf.Clamp(property.intValue, (int)minV, (int)maxV);
break;
}
}
PopCustomColors();
label.tooltip = "";
}
}
[CustomPropertyDrawer(typeof(FluffyUnderware.DevTools.MinMaxAttribute))]
public class MinMaxPropertyDrawer : DTPropertyDrawer<FluffyUnderware.DevTools.MinMaxAttribute>
{
float lBound;
float uBound;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
lBound = Attribute.Min;
uBound = Attribute.Max;
// Get maxV serialized
string[] path = property.Path();
path[path.Length - 1] = Attribute.MaxValueField;
SerializedProperty pMaxV = property.serializedObject.FindProperty(string.Join(".", path));
if (!string.IsNullOrEmpty(Attribute.MinBoundFieldOrPropertyName))
{
lBound = GetMemberValue<float>(property, Attribute.MinBoundFieldOrPropertyName);
}
if (!string.IsNullOrEmpty(Attribute.MaxBoundFieldOrPropertyName))
{
uBound = GetMemberValue<float>(property, Attribute.MaxBoundFieldOrPropertyName);
}
if (lBound > uBound)
{
float temp = lBound;
lBound = uBound;
uBound = temp;
}
float minV = 0;
float maxV = 0;
// GUI
EditorGUI.BeginChangeCheck();
switch (property.propertyType)
{
case SerializedPropertyType.Float:
minV = property.floatValue;
maxV = pMaxV.floatValue;
#if UNITY_PRE_5_5
EditorGUI.MinMaxSlider(label, position, ref minV, ref maxV, lBound, uBound);
#else
EditorGUI.MinMaxSlider(position, label, ref minV, ref maxV, lBound, uBound);
#endif
break;
case SerializedPropertyType.Integer:
minV = property.intValue;
maxV = pMaxV.intValue;
#if UNITY_PRE_5_5
EditorGUI.MinMaxSlider(label, position, ref minV, ref maxV, lBound, uBound);
#else
EditorGUI.MinMaxSlider(position, label, ref minV, ref maxV, lBound, uBound);
#endif
break;
}
if (EditorGUI.EndChangeCheck())
{
// Clamp
switch (property.propertyType)
{
case SerializedPropertyType.Float:
if (DT._UseSnapValuePrecision && Attribute.Precision > -1)
property.floatValue = Mathf.Max(lBound, DTMath.SnapPrecision(minV, Attribute.Precision));
pMaxV.floatValue = Mathf.Min(uBound, DTMath.SnapPrecision(maxV, Attribute.Precision));
break;
case SerializedPropertyType.Integer:
property.intValue = (int)minV;
pMaxV.intValue = (int)maxV;
break;
}
}
label.tooltip = "";
}
}
[CustomPropertyDrawer(typeof(MinAttribute), true)]
public class MinPropertyDrawer : DTPropertyDrawer<MinAttribute>
{
protected float minV;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
// GUI
EditorGUI.PropertyField(position, property, label);
minV = Attribute.MinValue;
if (!string.IsNullOrEmpty(Attribute.MinFieldOrPropertyName))
{
minV = GetMemberValue<float>(property, Attribute.MinFieldOrPropertyName);
}
// Clamp
switch (property.propertyType)
{
case SerializedPropertyType.Float:
if (DT._UseSnapValuePrecision && Attribute.Precision > -1)
property.floatValue = DTMath.SnapPrecision(property.floatValue, Attribute.Precision);
if (property.floatValue < minV)
property.floatValue = minV;
break;
case SerializedPropertyType.Integer:
if (property.intValue < minV)
property.intValue = (int)minV;
break;
}
label.tooltip = "";
}
}
[CustomPropertyDrawer(typeof(MaxAttribute))]
public class MaxPropertyDrawer : DTPropertyDrawer<MaxAttribute>
{
protected float maxV;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
// GUI
EditorGUI.PropertyField(position, property, label);
maxV = Attribute.MaxValue;
if (!string.IsNullOrEmpty(Attribute.MaxFieldOrPropertyName))
{
maxV = GetMemberValue<float>(property, Attribute.MaxFieldOrPropertyName);
}
// Clamp and snap
switch (property.propertyType)
{
case SerializedPropertyType.Float:
if (DT._UseSnapValuePrecision && Attribute.Precision > -1)
property.floatValue = DTMath.SnapPrecision(property.floatValue, Attribute.Precision);
if (property.floatValue > maxV)
property.floatValue = maxV;
break;
case SerializedPropertyType.Integer:
if (property.intValue > maxV)
property.intValue = (int)maxV;
break;
}
label.tooltip = "";
}
}
[CustomPropertyDrawer(typeof(PathSelectorAttribute))]
public class PathSelectorDrawer : DTPropertyDrawer<PathSelectorAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
if (property.propertyType == SerializedPropertyType.String)
{
position.width -= 20;
property.stringValue = EditorGUI.TextField(position, label, property.stringValue);
position.xMin = position.xMax;
position.width = 20;
if (GUI.Button(position, "..", EditorStyles.miniButton))
{
string v = string.Empty;
switch (Attribute.Mode)
{
case PathSelectorAttribute.DialogMode.OpenFile:
v = EditorUtility.OpenFilePanel(Attribute.Title, Attribute.Directory, Attribute.Extension);
break;
case PathSelectorAttribute.DialogMode.OpenFolder:
v = EditorUtility.OpenFolderPanel(Attribute.Title, Attribute.Directory, Attribute.DefaultName);
break;
case PathSelectorAttribute.DialogMode.CreateFile:
v = EditorUtility.SaveFilePanel(Attribute.Title, Attribute.Directory, Attribute.DefaultName, Attribute.Extension);
break;
}
if (!string.IsNullOrEmpty(v))
property.stringValue = v;
}
}
else
DTLog.LogError("[DevTools] PropertyAttribute [FileSelector] only works on string fields!");
}
}
[CustomPropertyDrawer(typeof(EnumFlagAttribute))]
public class EnumFlagDrawer : DTPropertyDrawer<EnumFlagAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
System.Enum targetEnum = GetPropertySourceField<System.Enum>(property);
EditorGUI.BeginProperty(position, label, property);
#if UNITY_2017_3_OR_NEWER
System.Enum enumNew = EditorGUI.EnumFlagsField(position, label, targetEnum);
#else
System.Enum enumNew = EditorGUI.EnumMaskField(position, label, targetEnum);
#endif
property.intValue = (int)System.Convert.ChangeType(enumNew, targetEnum.GetType(), System.Globalization.CultureInfo.InvariantCulture);
EditorGUI.EndProperty();
}
}
[CustomPropertyDrawer(typeof(EnumSelectionGridAttribute))]
public class EnumSelectionGridDrawer : DTPropertyDrawer<EnumSelectionGridAttribute>
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType == SerializedPropertyType.Enum)
{
string[] names = property.enumDisplayNames;
EditorGUI.PrefixLabel(position, label);
position = position.WithoutLabel();
EditorGUI.BeginChangeCheck();
int v = GUI.SelectionGrid(position, property.enumValueIndex, names, names.Length);
if (EditorGUI.EndChangeCheck())
property.enumValueIndex = v;
}
else
DTLog.LogError("[DevTools] PropertyAttribute [SelectionGrid] only works on Enum fields!");
}
}
[CustomPropertyDrawer(typeof(ObjectSelectorAttribute))]
public class ObjectSelectorDrawer : DTPropertyDrawer<ObjectSelectorAttribute>
{
}
[CustomPropertyDrawer(typeof(FloatRegionAttribute))]
public class FloatRegionPropertyDrawer : DTPropertyDrawer<FloatRegionAttribute>
{
PropertyInfo mPI;
object mObject;
SerializedProperty ppFrom;
SerializedProperty ppTo;
SerializedProperty ppSimpleValue;
RegionOptions<float> mOptions;
bool compact
{
get
{
return ((EditorGUIUtility.wideMode && Options == AttributeOptionsFlags.None) ||
((Options & AttributeOptionsFlags.Compact) == AttributeOptionsFlags.Compact ||
(Options & AttributeOptionsFlags.FullCompact) == AttributeOptionsFlags.FullCompact
)
);
}
}
bool minmax
{
get
{
return (Attribute.UseSlider && !ppSimpleValue.boolValue &&
mOptions.ClampFrom == DTValueClamping.Range && mOptions.ClampTo == DTValueClamping.Range &&
mOptions.FromMin == mOptions.ToMin && mOptions.FromMax == mOptions.ToMax);
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
Init(property);
float h = base.GetPropertyHeight(property, label);
if (!compact)
{
if (!Attribute.RegionIsOptional || !ppSimpleValue.boolValue || minmax)
h = (h + EditorGUIUtility.standardVerticalSpacing) * 2;
}
return h;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
Init(property);
Rect r = new Rect(position);
r.height = EditorGUIUtility.singleLineHeight;
if (minmax)
{
doMinmax(r, label, Attribute.RegionIsOptional);
}
else if (compact)
{
doCompact(r, label, new GUIContent(mOptions.LabelTo), Attribute.RegionIsOptional);
}
else
{
doRegular(r, label);
}
label.tooltip = "";
}
void doMinmax(Rect r, GUIContent label, bool showOptional)
{
float l = ppFrom.floatValue;
float u = ppTo.floatValue;
Rect r2 = new Rect(r);
if (showOptional)
r2.width -= 16;
EditorGUI.BeginChangeCheck();
#if UNITY_PRE_5_5
EditorGUI.MinMaxSlider(label, r2, ref l, ref u, mOptions.FromMin, mOptions.FromMax);
#else
EditorGUI.MinMaxSlider(r2, label, ref l, ref u, mOptions.FromMin, mOptions.FromMax);
#endif
if (EditorGUI.EndChangeCheck())
{
ppFrom.floatValue = l;
validateFrom();
ppTo.floatValue = u;
validateTo();
}
if (showOptional)
{
ppSimpleValue.boolValue = !EditorGUI.Toggle(new Rect(r.xMax - 16, r.y, 16, r.height), new GUIContent("", mOptions.OptionalTooltip), !ppSimpleValue.boolValue);
}
if (!compact)
{
r.y += r.height + EditorGUIUtility.standardVerticalSpacing;
doCompact(r, new GUIContent(mOptions.LabelFrom), new GUIContent(mOptions.LabelTo), false);
}
}
void doCompact(Rect r, GUIContent label, GUIContent labelTo, bool showOptional, bool fullLine = false)
{
float lw = EditorGUIUtility.labelWidth;
float fw = EditorGUIUtility.fieldWidth;
Rect r2 = new Rect(r);
bool showTo = !showOptional || !ppSimpleValue.boolValue;
float showToLabelWidth = DTGUI.LabelWidth(labelTo);
float chkWidth = showOptional ? 16 : 0;
if (fullLine)
{
EditorGUIUtility.labelWidth = DTGUI.LabelWidth(label);
lw = EditorGUIUtility.labelWidth;
}
r2.width = (r.width - lw - chkWidth - showToLabelWidth) / 2;
// From
r.width = lw + r2.width;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(r, ppFrom, label);
if (EditorGUI.EndChangeCheck())
validateFrom();
r.x += r.width;
// "To" may be optional
if (showOptional)
{
ppSimpleValue.boolValue = !EditorGUI.Toggle(new Rect(r.x, r.y, 16, r.height), new GUIContent("", mOptions.OptionalTooltip), !ppSimpleValue.boolValue);
r.x += 16;
}
// Show "To"?
if (showTo)
{
EditorGUI.BeginChangeCheck();
EditorGUIUtility.labelWidth = showToLabelWidth;
r.width = r2.width + showToLabelWidth;
ppTo.floatValue = EditorGUI.FloatField(r, labelTo, ppTo.floatValue);
if (EditorGUI.EndChangeCheck())
validateTo();
}
EditorGUIUtility.labelWidth = lw;
EditorGUIUtility.fieldWidth = fw;
}
void doRegular(Rect r, GUIContent label)
{
// From
Rect rF = new Rect(r);
if (Attribute.RegionIsOptional)
rF.width -= 16;
EditorGUI.BeginProperty(rF, label, ppFrom);
EditorGUI.BeginChangeCheck();
if (Attribute.UseSlider && mOptions.ClampFrom == DTValueClamping.Range)
ppFrom.floatValue = EditorGUI.Slider(rF, label, ppFrom.floatValue, mOptions.FromMin, mOptions.FromMax);
else
ppFrom.floatValue = EditorGUI.FloatField(rF, label, ppFrom.floatValue);
if (EditorGUI.EndChangeCheck())
validateFrom();
EditorGUI.EndProperty();
// Perhaps To is optional
if (Attribute.RegionIsOptional)
ppSimpleValue.boolValue = !EditorGUI.Toggle(new Rect(rF.x + rF.width + 1, rF.y, 16, rF.height), new GUIContent("", mOptions.OptionalTooltip), !ppSimpleValue.boolValue);
// To
if (!Attribute.RegionIsOptional || !ppSimpleValue.boolValue)
{
r.y += r.height + EditorGUIUtility.standardVerticalSpacing;
label.text = mOptions.LabelTo;
EditorGUI.BeginProperty(r, label, ppTo);
EditorGUI.BeginChangeCheck();
if (Attribute.UseSlider && mOptions.ClampTo == DTValueClamping.Range)
ppTo.floatValue = EditorGUI.Slider(r, label, ppTo.floatValue, mOptions.ToMin, mOptions.ToMax);
else
ppTo.floatValue = EditorGUI.FloatField(r, label, ppTo.floatValue);
if (EditorGUI.EndChangeCheck())
validateTo();
EditorGUI.EndProperty();
}
}
void validateFrom()
{
ppFrom.floatValue = DTMath.SnapPrecision(ppFrom.floatValue, Attribute.Precision);
switch (mOptions.ClampFrom)
{
case DTValueClamping.Min:
ppFrom.floatValue = Mathf.Max(mOptions.FromMin, ppFrom.floatValue);
break;
case DTValueClamping.Max:
ppFrom.floatValue = Mathf.Min(mOptions.FromMax, ppFrom.floatValue);
break;
case DTValueClamping.Range:
ppFrom.floatValue = Mathf.Clamp(ppFrom.floatValue, mOptions.FromMin, mOptions.FromMax);
if (minmax)
ppFrom.floatValue = Mathf.Min(ppFrom.floatValue, ppTo.floatValue);
break;
}
}
void validateTo()
{
ppTo.floatValue = DTMath.SnapPrecision(ppTo.floatValue, Attribute.Precision);
switch (mOptions.ClampTo)
{
case DTValueClamping.Min:
ppTo.floatValue = Mathf.Max(mOptions.ToMin, ppTo.floatValue);
break;
case DTValueClamping.Max:
ppTo.floatValue = Mathf.Min(mOptions.ToMax, ppTo.floatValue);
break;
case DTValueClamping.Range:
ppTo.floatValue = Mathf.Clamp(ppTo.floatValue, mOptions.ToMin, mOptions.ToMax);
if (minmax)
ppTo.floatValue = Mathf.Max(ppTo.floatValue, ppFrom.floatValue);
break;
}
}
void Init(SerializedProperty property)
{
try
{
mObject = property.GetParentObject();
if (mPI == null && !string.IsNullOrEmpty(Attribute.RegionOptionsPropertyName))
mPI = mObject.GetType().PropertyByName(Attribute.RegionOptionsPropertyName, true, true);
if (mPI != null)
mOptions = (RegionOptions<float>)mPI.GetValue(mObject, null);
else
mOptions = RegionOptions<float>.Default;
}
catch (System.Exception e)
{
DTLog.LogError("[DevTools] FloatRegionPropertyDrawer: Unable to find property '" + Attribute.RegionOptionsPropertyName + "'! (" + e.ToString() + ")");
}
ppFrom = property.FindPropertyRelative("From");
ppTo = property.FindPropertyRelative("To");
ppSimpleValue = property.FindPropertyRelative("SimpleValue");
if (minmax)
ppSimpleValue.boolValue = false;
}
}
[CustomPropertyDrawer(typeof(IntRegionAttribute))]
public class IntRegionPropertyDrawer : DTPropertyDrawer<IntRegionAttribute>
{
PropertyInfo mPI;
object mObject;
SerializedProperty ppFrom;
SerializedProperty ppTo;
SerializedProperty ppSimpleValue;
RegionOptions<int> mOptions;
bool compact
{
get
{
return (EditorGUIUtility.wideMode ||
((Options & AttributeOptionsFlags.Compact) == AttributeOptionsFlags.Compact ||
(Options & AttributeOptionsFlags.FullCompact) == AttributeOptionsFlags.FullCompact
)
);
}
}
bool minmax
{
get
{
return (Attribute.UseSlider && !Attribute.RegionIsOptional &&
mOptions.ClampFrom == DTValueClamping.Range && mOptions.ClampTo == DTValueClamping.Range &&
mOptions.FromMin == mOptions.ToMin && mOptions.FromMax == mOptions.ToMax);
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
Init(property);
float h = base.GetPropertyHeight(property, label);
if (!compact)
{
if (!Attribute.RegionIsOptional || !ppSimpleValue.boolValue || minmax)
h = (h + EditorGUIUtility.standardVerticalSpacing) * 2;
}
return h;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
base.OnGUI(position, property, label);
Init(property);
Rect r = new Rect(position);
r.height = EditorGUIUtility.singleLineHeight;
if (minmax)
{
doMinmax(r, label, Attribute.RegionIsOptional);
}
else if (compact)
{
doCompact(r, label, new GUIContent(mOptions.LabelTo), Attribute.RegionIsOptional);
}
else
{
doRegular(r, label);
}
label.tooltip = "";
}
void doMinmax(Rect r, GUIContent label, bool showOptional)
{
float l = ppFrom.intValue;
float u = ppTo.intValue;
Rect r2 = new Rect(r);
if (showOptional)
r2.width -= 16;
EditorGUI.BeginChangeCheck();
#if UNITY_PRE_5_5
EditorGUI.MinMaxSlider(label, r2, ref l, ref u, mOptions.FromMin, mOptions.FromMax);
#else
EditorGUI.MinMaxSlider(r2, label, ref l, ref u, mOptions.FromMin, mOptions.FromMax);
#endif
if (EditorGUI.EndChangeCheck())
{
ppFrom.intValue = (int)l;
validateFrom();
ppTo.intValue = (int)u;
validateTo();
}
if (showOptional)
{
ppSimpleValue.boolValue = !EditorGUI.Toggle(new Rect(r.xMax - 16, r.y, 16, r.height), new GUIContent("", mOptions.OptionalTooltip), !ppSimpleValue.boolValue);
}
if (!compact)
{
r.y += r.height + EditorGUIUtility.standardVerticalSpacing;
doCompact(r, new GUIContent(mOptions.LabelFrom), new GUIContent(mOptions.LabelTo), false);
}
}
void doCompact(Rect r, GUIContent label, GUIContent labelTo, bool showOptional, bool fullLine = false)
{
float lw = EditorGUIUtility.labelWidth;
float fw = EditorGUIUtility.fieldWidth;
Rect r2 = new Rect(r);
bool showTo = !showOptional || !ppSimpleValue.boolValue;
float showToLabelWidth = DTGUI.LabelWidth(labelTo);
float chkWidth = showOptional ? 16 : 0;
if (fullLine)
{
EditorGUIUtility.labelWidth = DTGUI.LabelWidth(label);
lw = EditorGUIUtility.labelWidth;
}
r2.width = (r.width - lw - chkWidth - showToLabelWidth) / 2;
// From
r.width = lw + r2.width;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(r, ppFrom, label);
if (EditorGUI.EndChangeCheck())
validateFrom();
r.x += r.width;
// "To" may be optional
if (showOptional)
{
ppSimpleValue.boolValue = !EditorGUI.Toggle(new Rect(r.x, r.y, 16, r.height), new GUIContent("", mOptions.OptionalTooltip), !ppSimpleValue.boolValue);
r.x += 16;
}
// Show "To"?
if (showTo)
{
EditorGUI.BeginChangeCheck();
EditorGUIUtility.labelWidth = showToLabelWidth;
r.width = r2.width + showToLabelWidth;
ppTo.intValue = EditorGUI.IntField(r, labelTo, ppTo.intValue);
if (EditorGUI.EndChangeCheck())
validateTo();
}
EditorGUIUtility.labelWidth = lw;
EditorGUIUtility.fieldWidth = fw;
}
void doRegular(Rect r, GUIContent label)
{
// From
Rect rF = new Rect(r);
if (Attribute.RegionIsOptional)
rF.width -= 16;
EditorGUI.BeginProperty(rF, label, ppFrom);
EditorGUI.BeginChangeCheck();
if (Attribute.UseSlider && mOptions.ClampFrom == DTValueClamping.Range)
ppFrom.intValue = EditorGUI.IntSlider(rF, label, ppFrom.intValue, mOptions.FromMin, mOptions.FromMax);
else
ppFrom.intValue = EditorGUI.IntField(rF, label, ppFrom.intValue);
if (EditorGUI.EndChangeCheck())
validateFrom();
EditorGUI.EndProperty();
// Perhaps To is optional
if (Attribute.RegionIsOptional)
ppSimpleValue.boolValue = !EditorGUI.Toggle(new Rect(rF.x + rF.width, rF.y, 16, rF.height), new GUIContent("", mOptions.OptionalTooltip), !ppSimpleValue.boolValue);
// To
if (!Attribute.RegionIsOptional || !ppSimpleValue.boolValue)
{
r.y += r.height + EditorGUIUtility.standardVerticalSpacing;
label.text = mOptions.LabelTo;
EditorGUI.BeginProperty(r, label, ppTo);
EditorGUI.BeginChangeCheck();
if (Attribute.UseSlider && mOptions.ClampTo == DTValueClamping.Range)
ppTo.intValue = EditorGUI.IntSlider(r, label, ppTo.intValue, mOptions.ToMin, mOptions.ToMax);
else
ppTo.intValue = EditorGUI.IntField(r, label, ppTo.intValue);
if (EditorGUI.EndChangeCheck())
validateTo();
EditorGUI.EndProperty();
}
}
void validateFrom()
{
switch (mOptions.ClampFrom)
{
case DTValueClamping.Min:
ppFrom.intValue = Mathf.Max(mOptions.FromMin, ppFrom.intValue);
break;
case DTValueClamping.Max:
ppFrom.intValue = Mathf.Min(mOptions.FromMax, ppFrom.intValue);
break;
case DTValueClamping.Range:
ppFrom.intValue = Mathf.Clamp(ppFrom.intValue, mOptions.FromMin, mOptions.FromMax);
if (minmax)
ppFrom.intValue = Mathf.Min(ppFrom.intValue, ppTo.intValue);
break;
}
}
void validateTo()
{
switch (mOptions.ClampTo)
{
case DTValueClamping.Min:
ppTo.intValue = Mathf.Max(mOptions.ToMin, ppTo.intValue);
break;
case DTValueClamping.Max:
ppTo.intValue = Mathf.Min(mOptions.ToMax, ppTo.intValue);
break;
case DTValueClamping.Range:
ppTo.intValue = Mathf.Clamp(ppTo.intValue, mOptions.ToMin, mOptions.ToMax);
if (minmax)
ppTo.intValue = Mathf.Max(ppTo.intValue, ppFrom.intValue);
break;
}
}
void Init(SerializedProperty property)
{
try
{
mObject = property.GetParentObject();
if (mPI == null && !string.IsNullOrEmpty(Attribute.RegionOptionsPropertyName))
mPI = mObject.GetType().PropertyByName(Attribute.RegionOptionsPropertyName, true, true);
if (mPI != null)
mOptions = (RegionOptions<int>)mPI.GetValue(mObject, null);
else
mOptions = RegionOptions<int>.Default;
}
catch (System.Exception e)
{
DTLog.LogError("[DevTools] IntRegionPropertyDrawer: Unable to find property '" + Attribute.RegionOptionsPropertyName + "'! (" + e.ToString() + ")");
}
ppFrom = property.FindPropertyRelative("From");
ppTo = property.FindPropertyRelative("To");
ppSimpleValue = property.FindPropertyRelative("SimpleValue");
}
}
}