123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- using System;
- using System.Reflection;
- using UnityEngine;
- using System.Text.RegularExpressions;
- using System.Collections;
- using System.Linq;
- #if UNITY_EDITOR
- using UnityEditor;
- #endif
- namespace UniRx
- {
- [System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
- public class InspectorDisplayAttribute : PropertyAttribute
- {
- public string FieldName { get; private set; }
- public bool NotifyPropertyChanged { get; private set; }
- public InspectorDisplayAttribute(string fieldName = "value", bool notifyPropertyChanged = true)
- {
- FieldName = fieldName;
- NotifyPropertyChanged = notifyPropertyChanged;
- }
- }
- /// <summary>
- /// Enables multiline input field for StringReactiveProperty. Default line is 3.
- /// </summary>
- [System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
- public class MultilineReactivePropertyAttribute : PropertyAttribute
- {
- public int Lines { get; private set; }
- public MultilineReactivePropertyAttribute()
- {
- Lines = 3;
- }
- public MultilineReactivePropertyAttribute(int lines)
- {
- this.Lines = lines;
- }
- }
- /// <summary>
- /// Enables range input field for Int/FloatReactiveProperty.
- /// </summary>
- [System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
- public class RangeReactivePropertyAttribute : PropertyAttribute
- {
- public float Min { get; private set; }
- public float Max { get; private set; }
- public RangeReactivePropertyAttribute(float min, float max)
- {
- this.Min = min;
- this.Max = max;
- }
- }
- #if UNITY_EDITOR
- // InspectorDisplay and for Specialized ReactiveProperty
- // If you want to customize other specialized ReactiveProperty
- // [UnityEditor.CustomPropertyDrawer(typeof(YourSpecializedReactiveProperty))]
- // public class ExtendInspectorDisplayDrawer : InspectorDisplayDrawer { }
- [UnityEditor.CustomPropertyDrawer(typeof(InspectorDisplayAttribute))]
- [UnityEditor.CustomPropertyDrawer(typeof(IntReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(LongReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(ByteReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(FloatReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(DoubleReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(StringReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(BoolReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(Vector2ReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(Vector3ReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(Vector4ReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(ColorReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(RectReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(AnimationCurveReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(BoundsReactiveProperty))]
- [UnityEditor.CustomPropertyDrawer(typeof(QuaternionReactiveProperty))]
- public class InspectorDisplayDrawer : UnityEditor.PropertyDrawer
- {
- public override void OnGUI(Rect position, UnityEditor.SerializedProperty property, GUIContent label)
- {
- string fieldName;
- bool notifyPropertyChanged;
- {
- var attr = this.attribute as InspectorDisplayAttribute;
- fieldName = (attr == null) ? "value" : attr.FieldName;
- notifyPropertyChanged = (attr == null) ? true : attr.NotifyPropertyChanged;
- }
- if (notifyPropertyChanged)
- {
- EditorGUI.BeginChangeCheck();
- }
- var targetSerializedProperty = property.FindPropertyRelative(fieldName);
- if (targetSerializedProperty == null)
- {
- UnityEditor.EditorGUI.LabelField(position, label, new GUIContent() { text = "InspectorDisplay can't find target:" + fieldName });
- if (notifyPropertyChanged)
- {
- EditorGUI.EndChangeCheck();
- }
- return;
- }
- else
- {
- EmitPropertyField(position, targetSerializedProperty, label);
- }
- if (notifyPropertyChanged)
- {
- if (EditorGUI.EndChangeCheck())
- {
- property.serializedObject.ApplyModifiedProperties(); // deserialize to field
- var paths = property.propertyPath.Split('.'); // X.Y.Z...
- var attachedComponent = property.serializedObject.targetObject;
- var targetProp = (paths.Length == 1)
- ? fieldInfo.GetValue(attachedComponent)
- : GetValueRecursive(attachedComponent, 0, paths);
- if (targetProp == null) return;
- var propInfo = targetProp.GetType().GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- var modifiedValue = propInfo.GetValue(targetProp, null); // retrieve new value
- var methodInfo = targetProp.GetType().GetMethod("SetValueAndForceNotify", BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- if (methodInfo != null)
- {
- methodInfo.Invoke(targetProp, new object[] { modifiedValue });
- }
- }
- else
- {
- property.serializedObject.ApplyModifiedProperties();
- }
- }
- }
- object GetValueRecursive(object obj, int index, string[] paths)
- {
- var path = paths[index];
- FieldInfo fldInfo = null;
- var type = obj.GetType();
- while (fldInfo == null)
- {
- // attempt to get information about the field
- fldInfo = type.GetField(path, BindingFlags.IgnoreCase | BindingFlags.GetField | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- if (fldInfo != null ||
- type.BaseType == null ||
- type.BaseType.IsSubclassOf(typeof(ReactiveProperty<>))) break;
- // if the field information is missing, it may be in the base class
- type = type.BaseType;
- }
- // If array, path = Array.data[index]
- if (fldInfo == null && path == "Array")
- {
- try
- {
- path = paths[++index];
- var m = Regex.Match(path, @"(.+)\[([0-9]+)*\]");
- var arrayIndex = int.Parse(m.Groups[2].Value);
- var arrayValue = (obj as System.Collections.IList)[arrayIndex];
- if (index < paths.Length - 1)
- {
- return GetValueRecursive(arrayValue, ++index, paths);
- }
- else
- {
- return arrayValue;
- }
- }
- catch
- {
- Debug.Log("InspectorDisplayDrawer Exception, objType:" + obj.GetType().Name + " path:" + string.Join(", ", paths));
- throw;
- }
- }
- else if (fldInfo == null)
- {
- throw new Exception("Can't decode path, please report to UniRx's GitHub issues:" + string.Join(", ", paths));
- }
- var v = fldInfo.GetValue(obj);
- if (index < paths.Length - 1)
- {
- return GetValueRecursive(v, ++index, paths);
- }
- return v;
- }
- public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
- {
- var attr = this.attribute as InspectorDisplayAttribute;
- var fieldName = (attr == null) ? "value" : attr.FieldName;
- var height = base.GetPropertyHeight(property, label);
- var valueProperty = property.FindPropertyRelative(fieldName);
- if (valueProperty == null)
- {
- return height;
- }
- if (valueProperty.propertyType == SerializedPropertyType.Rect)
- {
- return height * 2;
- }
- if (valueProperty.propertyType == SerializedPropertyType.Bounds)
- {
- return height * 3;
- }
- if (valueProperty.propertyType == SerializedPropertyType.String)
- {
- var multilineAttr = GetMultilineAttribute();
- if (multilineAttr != null)
- {
- return ((!EditorGUIUtility.wideMode) ? 16f : 0f) + 16f + (float)((multilineAttr.Lines - 1) * 13);
- };
- }
- if (valueProperty.isExpanded)
- {
- var count = 0;
- var e = valueProperty.GetEnumerator();
- while (e.MoveNext()) count++;
- return ((height + 4) * count) + 6; // (Line = 20 + Padding) ?
- }
- return height;
- }
- protected virtual void EmitPropertyField(Rect position, UnityEditor.SerializedProperty targetSerializedProperty, GUIContent label)
- {
- var multiline = GetMultilineAttribute();
- if (multiline == null)
- {
- var range = GetRangeAttribute();
- if (range == null)
- {
- UnityEditor.EditorGUI.PropertyField(position, targetSerializedProperty, label, includeChildren: true);
- }
- else
- {
- if (targetSerializedProperty.propertyType == SerializedPropertyType.Float)
- {
- EditorGUI.Slider(position, targetSerializedProperty, range.Min, range.Max, label);
- }
- else if (targetSerializedProperty.propertyType == SerializedPropertyType.Integer)
- {
- EditorGUI.IntSlider(position, targetSerializedProperty, (int)range.Min, (int)range.Max, label);
- }
- else
- {
- EditorGUI.LabelField(position, label.text, "Use Range with float or int.");
- }
- }
- }
- else
- {
- var property = targetSerializedProperty;
- label = EditorGUI.BeginProperty(position, label, property);
- var method = typeof(EditorGUI).GetMethod("MultiFieldPrefixLabel", BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.NonPublic);
- position = (Rect)method.Invoke(null, new object[] { position, 0, label, 1 });
- EditorGUI.BeginChangeCheck();
- int indentLevel = EditorGUI.indentLevel;
- EditorGUI.indentLevel = 0;
- var stringValue = EditorGUI.TextArea(position, property.stringValue);
- EditorGUI.indentLevel = indentLevel;
- if (EditorGUI.EndChangeCheck())
- {
- property.stringValue = stringValue;
- }
- EditorGUI.EndProperty();
- }
- }
- MultilineReactivePropertyAttribute GetMultilineAttribute()
- {
- var fi = this.fieldInfo;
- if (fi == null) return null;
- return fi.GetCustomAttributes(false).OfType<MultilineReactivePropertyAttribute>().FirstOrDefault();
- }
- RangeReactivePropertyAttribute GetRangeAttribute()
- {
- var fi = this.fieldInfo;
- if (fi == null) return null;
- return fi.GetCustomAttributes(false).OfType<RangeReactivePropertyAttribute>().FirstOrDefault();
- }
- }
- #endif
- }
|