summaryrefslogtreecommitdiff
path: root/ActiveRagdoll/Assets/ThirdParty/SerializableCallback/Editor/SerializableCallbackDrawer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ActiveRagdoll/Assets/ThirdParty/SerializableCallback/Editor/SerializableCallbackDrawer.cs')
-rw-r--r--ActiveRagdoll/Assets/ThirdParty/SerializableCallback/Editor/SerializableCallbackDrawer.cs327
1 files changed, 327 insertions, 0 deletions
diff --git a/ActiveRagdoll/Assets/ThirdParty/SerializableCallback/Editor/SerializableCallbackDrawer.cs b/ActiveRagdoll/Assets/ThirdParty/SerializableCallback/Editor/SerializableCallbackDrawer.cs
new file mode 100644
index 0000000..561796c
--- /dev/null
+++ b/ActiveRagdoll/Assets/ThirdParty/SerializableCallback/Editor/SerializableCallbackDrawer.cs
@@ -0,0 +1,327 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+[CustomPropertyDrawer(typeof(TargetConstraintAttribute))]
+[CustomPropertyDrawer(typeof(SerializableCallbackBase), true)]
+public class SerializableCallbackDrawer : PropertyDrawer {
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
+ // Without this, you can't edit fields above the SerializedProperty
+ property.serializedObject.ApplyModifiedProperties();
+
+ // Indent label
+ label.text = " " + label.text;
+
+#if UNITY_2019_1_OR_NEWER
+ GUI.Box(position, "");
+#else
+ GUI.Box(position, "", (GUIStyle)
+ "flow overlay box");
+#endif
+ position.y += 4;
+ // Using BeginProperty / EndProperty on the parent property means that
+ // prefab override logic works on the entire property.
+ property.serializedObject.Update();
+ EditorGUI.BeginProperty(position, label, property);
+ // Draw label
+ Rect pos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
+
+ Rect targetRect = new Rect(pos.x, pos.y, pos.width, EditorGUIUtility.singleLineHeight);
+
+ // Get target
+ SerializedProperty targetProp = property.FindPropertyRelative("_target");
+ object target = targetProp.objectReferenceValue;
+ if (attribute != null && attribute is TargetConstraintAttribute) {
+ Type targetType = (attribute as TargetConstraintAttribute).targetType;
+ EditorGUI.ObjectField(targetRect, targetProp, targetType, GUIContent.none);
+ } else EditorGUI.PropertyField(targetRect, targetProp, GUIContent.none);
+
+ if (target == null) {
+ Rect helpBoxRect = new Rect(position.x + 8, targetRect.max.y + EditorGUIUtility.standardVerticalSpacing, position.width - 16, EditorGUIUtility.singleLineHeight);
+ string msg = "Call not set. Execution will be slower.";
+ EditorGUI.HelpBox(helpBoxRect, msg, MessageType.Warning);
+ } else if (target is MonoScript) {
+ Rect helpBoxRect = new Rect(position.x + 8, targetRect.max.y + EditorGUIUtility.standardVerticalSpacing, position.width - 16, EditorGUIUtility.singleLineHeight + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
+ string msg = "Assign a GameObject, Component or a ScriptableObject, not a script.";
+ EditorGUI.HelpBox(helpBoxRect, msg, MessageType.Warning);
+ } else {
+ int indent = EditorGUI.indentLevel;
+ EditorGUI.indentLevel++;
+
+ // Get method name
+ SerializedProperty methodProp = property.FindPropertyRelative("_methodName");
+ string methodName = methodProp.stringValue;
+
+ // Get args
+ SerializedProperty argProps = property.FindPropertyRelative("_args");
+ Type[] argTypes = GetArgTypes(argProps);
+
+ // Get dynamic
+ SerializedProperty dynamicProp = property.FindPropertyRelative("_dynamic");
+ bool dynamic = dynamicProp.boolValue;
+
+ // Get active method
+ MethodInfo activeMethod = GetMethod(target, methodName, argTypes);
+
+ GUIContent methodlabel = new GUIContent("n/a");
+ if (activeMethod != null) methodlabel = new GUIContent(PrettifyMethod(activeMethod));
+ else if (!string.IsNullOrEmpty(methodName)) methodlabel = new GUIContent("Missing (" + PrettifyMethod(methodName, argTypes) + ")");
+
+ Rect methodRect = new Rect(position.x, targetRect.max.y + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);
+
+ // Method select button
+ pos = EditorGUI.PrefixLabel(methodRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(dynamic ? "Method (dynamic)" : "Method"));
+ if (EditorGUI.DropdownButton(pos, methodlabel, FocusType.Keyboard)) {
+ MethodSelector(property);
+ }
+
+ if (activeMethod != null && !dynamic) {
+ // Args
+ ParameterInfo[] activeParameters = activeMethod.GetParameters();
+ Rect argRect = new Rect(position.x, methodRect.max.y + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);
+ string[] types = new string[argProps.arraySize];
+ for (int i = 0; i < types.Length; i++) {
+ SerializedProperty argProp = argProps.FindPropertyRelative("Array.data[" + i + "]");
+ GUIContent argLabel = new GUIContent(ObjectNames.NicifyVariableName(activeParameters[i].Name));
+
+ EditorGUI.BeginChangeCheck();
+ switch ((Arg.ArgType) argProp.FindPropertyRelative("argType").enumValueIndex) {
+ case Arg.ArgType.Bool:
+ EditorGUI.PropertyField(argRect, argProp.FindPropertyRelative("boolValue"), argLabel);
+ break;
+ case Arg.ArgType.Int:
+ EditorGUI.PropertyField(argRect, argProp.FindPropertyRelative("intValue"), argLabel);
+ break;
+ case Arg.ArgType.Float:
+ EditorGUI.PropertyField(argRect, argProp.FindPropertyRelative("floatValue"), argLabel);
+ break;
+ case Arg.ArgType.String:
+ EditorGUI.PropertyField(argRect, argProp.FindPropertyRelative("stringValue"), argLabel);
+ break;
+ case Arg.ArgType.Object:
+ SerializedProperty typeProp = argProp.FindPropertyRelative("_typeName");
+ SerializedProperty objProp = argProp.FindPropertyRelative("objectValue");
+
+ if (typeProp != null) {
+ Type objType = Type.GetType(typeProp.stringValue, false);
+ EditorGUI.BeginChangeCheck();
+ Object obj = objProp.objectReferenceValue;
+ obj = EditorGUI.ObjectField(argRect, argLabel, obj, objType, true);
+ if (EditorGUI.EndChangeCheck()) {
+ objProp.objectReferenceValue = obj;
+ }
+ } else {
+ EditorGUI.PropertyField(argRect, objProp, argLabel);
+ }
+ break;
+ }
+ if (EditorGUI.EndChangeCheck()) {
+ property.FindPropertyRelative("dirty").boolValue = true;
+ }
+ argRect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
+ }
+ }
+ EditorGUI.indentLevel = indent;
+ }
+
+ // Set indent back to what it was
+ EditorGUI.EndProperty();
+ property.serializedObject.ApplyModifiedProperties();
+ }
+
+ private class MenuItem {
+ public GenericMenu.MenuFunction action;
+ public string path;
+ public GUIContent label;
+
+ public MenuItem(string path, string name, GenericMenu.MenuFunction action) {
+ this.action = action;
+ this.label = new GUIContent(path + '/' + name);
+ this.path = path;
+ }
+ }
+ void MethodSelector(SerializedProperty property) {
+ // Return type constraint
+ Type returnType = null;
+ // Arg type constraint
+ Type[] argTypes = new Type[0];
+
+ // Get return type and argument constraints
+ SerializableCallbackBase dummy = GetDummyFunction(property);
+ Type[] genericTypes = dummy.GetType().BaseType.GetGenericArguments();
+ // SerializableEventBase is always void return type
+ if (dummy is SerializableEventBase) {
+ returnType = typeof(void);
+ if (genericTypes.Length > 0) {
+ argTypes = new Type[genericTypes.Length];
+ Array.Copy(genericTypes, argTypes, genericTypes.Length);
+ }
+ } else {
+ if (genericTypes != null && genericTypes.Length > 0) {
+ // The last generic argument is the return type
+ returnType = genericTypes[genericTypes.Length - 1];
+ if (genericTypes.Length > 1) {
+ argTypes = new Type[genericTypes.Length - 1];
+ Array.Copy(genericTypes, argTypes, genericTypes.Length - 1);
+ }
+ }
+ }
+
+ SerializedProperty targetProp = property.FindPropertyRelative("_target");
+
+ List<MenuItem> dynamicItems = new List<MenuItem>();
+ List<MenuItem> staticItems = new List<MenuItem>();
+
+ List<Object> targets = new List<Object>() { targetProp.objectReferenceValue };
+
+ if (targets[0] is Component) {
+ targets = (targets[0] as Component).gameObject.GetComponents<Component>().ToList<Object>();
+ targets.Add((targetProp.objectReferenceValue as Component).gameObject);
+ } else if (targets[0] is GameObject) {
+ targets = (targets[0] as GameObject).GetComponents<Component>().ToList<Object>();
+ targets.Add(targetProp.objectReferenceValue as GameObject);
+ }
+ for (int c = 0; c < targets.Count; c++) {
+ Object t = targets[c];
+ MethodInfo[] methods = targets[c].GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
+
+ for (int i = 0; i < methods.Length; i++) {
+ MethodInfo method = methods[i];
+
+ // Skip methods with wrong return type
+ if (returnType != null && method.ReturnType != returnType) continue;
+ // Skip methods with null return type
+ // if (method.ReturnType == typeof(void)) continue;
+ // Skip generic methods
+ if (method.IsGenericMethod) continue;
+
+ Type[] parms = method.GetParameters().Select(x => x.ParameterType).ToArray();
+
+ // Skip methods with more than 4 args
+ if (parms.Length > 4) continue;
+ // Skip methods with unsupported args
+ if (parms.Any(x => !Arg.IsSupported(x))) continue;
+
+ string methodPrettyName = PrettifyMethod(methods[i]);
+ staticItems.Add(new MenuItem(targets[c].GetType().Name + "/" + methods[i].DeclaringType.Name, methodPrettyName, () => SetMethod(property, t, method, false)));
+
+ // Skip methods with wrong constrained args
+ if (argTypes.Length == 0 || !Enumerable.SequenceEqual(argTypes, parms)) continue;
+
+ dynamicItems.Add(new MenuItem(targets[c].GetType().Name + "/" + methods[i].DeclaringType.Name, methods[i].Name, () => SetMethod(property, t, method, true)));
+ }
+ }
+
+ // Construct and display context menu
+ GenericMenu menu = new GenericMenu();
+ if (dynamicItems.Count > 0) {
+ string[] paths = dynamicItems.GroupBy(x => x.path).Select(x => x.First().path).ToArray();
+ foreach (string path in paths) {
+ menu.AddItem(new GUIContent(path + "/Dynamic " + PrettifyTypes(argTypes)), false, null);
+ }
+ for (int i = 0; i < dynamicItems.Count; i++) {
+ menu.AddItem(dynamicItems[i].label, false, dynamicItems[i].action);
+ }
+ foreach (string path in paths) {
+ menu.AddItem(new GUIContent(path + "/ "), false, null);
+ menu.AddItem(new GUIContent(path + "/Static parameters"), false, null);
+ }
+ }
+ for (int i = 0; i < staticItems.Count; i++) {
+ menu.AddItem(staticItems[i].label, false, staticItems[i].action);
+ }
+ if (menu.GetItemCount() == 0) menu.AddDisabledItem(new GUIContent("No methods with return type '" + GetTypeName(returnType) + "'"));
+ menu.ShowAsContext();
+ }
+
+ string PrettifyMethod(string methodName, Type[] parmTypes) {
+ string parmnames = PrettifyTypes(parmTypes);
+ return methodName + "(" + parmnames + ")";
+ }
+
+ string PrettifyMethod(MethodInfo methodInfo) {
+ if (methodInfo == null) throw new ArgumentNullException("methodInfo");
+ ParameterInfo[] parms = methodInfo.GetParameters();
+ string parmnames = PrettifyTypes(parms.Select(x => x.ParameterType).ToArray());
+ return GetTypeName(methodInfo.ReturnParameter.ParameterType) + " " + methodInfo.Name + "(" + parmnames + ")";
+ }
+
+ string PrettifyTypes(Type[] types) {
+ if (types == null) throw new ArgumentNullException("types");
+ return string.Join(", ", types.Select(x => GetTypeName(x)).ToArray());
+ }
+
+ MethodInfo GetMethod(object target, string methodName, Type[] types) {
+ MethodInfo activeMethod = target.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, null, CallingConventions.Any, types, null);
+ return activeMethod;
+ }
+
+ private Type[] GetArgTypes(SerializedProperty argsProp) {
+ Type[] types = new Type[argsProp.arraySize];
+ for (int i = 0; i < argsProp.arraySize; i++) {
+ SerializedProperty argProp = argsProp.GetArrayElementAtIndex(i);
+ SerializedProperty typeNameProp = argProp.FindPropertyRelative("_typeName");
+ if (typeNameProp != null) types[i] = Type.GetType(typeNameProp.stringValue, false);
+ if (types[i] == null) types[i] = Arg.RealType((Arg.ArgType) argProp.FindPropertyRelative("argType").enumValueIndex);
+ }
+ return types;
+ }
+
+ private void SetMethod(SerializedProperty property, UnityEngine.Object target, MethodInfo methodInfo, bool dynamic) {
+ SerializedProperty targetProp = property.FindPropertyRelative("_target");
+ targetProp.objectReferenceValue = target;
+ SerializedProperty methodProp = property.FindPropertyRelative("_methodName");
+ methodProp.stringValue = methodInfo.Name;
+ SerializedProperty dynamicProp = property.FindPropertyRelative("_dynamic");
+ dynamicProp.boolValue = dynamic;
+ SerializedProperty argProp = property.FindPropertyRelative("_args");
+ ParameterInfo[] parameters = methodInfo.GetParameters();
+ argProp.arraySize = parameters.Length;
+ for (int i = 0; i < parameters.Length; i++) {
+ argProp.FindPropertyRelative("Array.data[" + i + "].argType").enumValueIndex = (int) Arg.FromRealType(parameters[i].ParameterType);
+ argProp.FindPropertyRelative("Array.data[" + i + "]._typeName").stringValue = parameters[i].ParameterType.AssemblyQualifiedName;
+ }
+ property.FindPropertyRelative("dirty").boolValue = true;
+ property.serializedObject.ApplyModifiedProperties();
+ property.serializedObject.Update();
+ }
+
+ private static string GetTypeName(Type t) {
+ if (t == typeof(int)) return "int";
+ else if (t == typeof(float)) return "float";
+ else if (t == typeof(string)) return "string";
+ else if (t == typeof(bool)) return "bool";
+ else if (t == typeof(void)) return "void";
+ else return t.Name;
+ }
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
+ float lineheight = EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.singleLineHeight;
+ SerializedProperty targetProp = property.FindPropertyRelative("_target");
+ SerializedProperty argProps = property.FindPropertyRelative("_args");
+ SerializedProperty dynamicProp = property.FindPropertyRelative("_dynamic");
+ float height = lineheight + lineheight;
+ if (targetProp.objectReferenceValue != null && targetProp.objectReferenceValue is MonoScript) height += lineheight;
+ else if (targetProp.objectReferenceValue != null && !dynamicProp.boolValue) height += argProps.arraySize * lineheight;
+ height += 8;
+ return height;
+ }
+
+ private static SerializableCallbackBase GetDummyFunction(SerializedProperty prop) {
+ string stringValue = prop.FindPropertyRelative("_typeName").stringValue;
+ Type type = Type.GetType(stringValue, false);
+ SerializableCallbackBase result;
+ if (type == null) {
+ return null;
+ } else {
+ result = (Activator.CreateInstance(type) as SerializableCallbackBase);
+ }
+ return result;
+ }
+}