diff options
Diffstat (limited to 'Assets/Plugins/AdvancedInspector/Core/ActionBinding.cs')
-rw-r--r-- | Assets/Plugins/AdvancedInspector/Core/ActionBinding.cs | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/Assets/Plugins/AdvancedInspector/Core/ActionBinding.cs b/Assets/Plugins/AdvancedInspector/Core/ActionBinding.cs new file mode 100644 index 00000000..55573bbd --- /dev/null +++ b/Assets/Plugins/AdvancedInspector/Core/ActionBinding.cs @@ -0,0 +1,744 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +using UnityEngine; + +namespace AdvancedInspector +{ + /// <summary> + /// This class represent an event, and allows the Inspector to bind it to another method. + /// Arguments can be sent to the invoked method from a static field, internal from the code invoking, or from another method returned value. + /// </summary> + [Serializable, AdvancedInspector] + public class ActionBinding : ICopiable + { + [SerializeField] + private string[] internalParameters = new string[0]; + + [SerializeField] + private GameObject gameObject; + + [Inspect] + public GameObject GameObject + { + get { return gameObject; } + set + { + if (gameObject != value) + { + gameObject = value; + Component = null; + } + } + } + + [SerializeField] + private Component component; + + [Inspect, Restrict("GetComponents")] + public Component Component + { + get { return component; } + set + { + if (component != value) + { + component = value; + Method = null; + } + } + } + + private IList GetComponents() + { + List<DescriptorPair> components = new List<DescriptorPair>(); + if (gameObject == null) + return components; + + foreach (Component component in gameObject.GetComponents(typeof(Component))) + components.Add(new DescriptorPair(component, new DescriptorAttribute(component.GetType().Name, ""))); + + return components; + } + + [SerializeField] + private string method; + + [Inspect, Restrict("GetMethods", RestrictDisplay.Toolbox)] + public MethodInfo Method + { + get { return GetMethodInfo(); } + set + { + if (value == null) + { + parameters = new BindingParameter[0]; + method = ""; + return; + } + + MethodInfo info = GetMethodInfo(); + if (info != value) + { + method = value.Name; + ParameterInfo[] param = value.GetParameters(); + parameters = new BindingParameter[param.Length]; + for (int i = 0; i < param.Length; i++) + { + parameters[i] = new BindingParameter(internalParameters.Length > i); + parameters[i].Type = param[i].ParameterType; + if (internalParameters.Length > i) + parameters[i].binding = BindingParameter.BindingType.Internal; + } + } + } + } + + private IList GetMethods() + { + List<DescriptorPair> methods = new List<DescriptorPair>(); + if (gameObject == null || component == null) + return methods; + + foreach (MethodInfo info in component.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) + { + if (info.IsGenericMethod || info.IsConstructor || info.IsFinal || info.IsSpecialName || info.DeclaringType == typeof(object) || info.DeclaringType == typeof(Component)) + continue; + + if (!IsMethodValid(info)) + continue; + + ParameterInfo[] param = info.GetParameters(); + + methods.Add(new DescriptorPair(info, new DescriptorAttribute(GetParamNames(info.Name, param), ""))); + } + + foreach (PropertyInfo info in component.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) + { + if (!info.CanWrite || info.DeclaringType == typeof(object) || info.DeclaringType == typeof(UnityEngine.Object) || info.DeclaringType == typeof(Component)) + continue; + + MethodInfo method = info.GetSetMethod(); + if (method == null || !IsMethodValid(method)) + continue; + + ParameterInfo[] param = method.GetParameters(); + + methods.Add(new DescriptorPair(method, new DescriptorAttribute(GetParamNames(info.Name, param), ""))); + } + + return methods; + } + + private bool IsMethodValid(MethodInfo info) + { + ParameterInfo[] param = info.GetParameters(); + + bool valid = true; + for (int i = 0; i < param.Length; i++) + { + if (!BindingParameter.IsValidType(param[i].ParameterType)) + { + valid = false; + break; + } + + if (internalParameters.Length > i) + { + Type type = Type.GetType(internalParameters[i]); + if (!type.IsAssignableFrom(param[i].ParameterType)) + { + valid = false; + break; + } + } + } + + return valid; + } + + private string GetParamNames(string name, ParameterInfo[] param) + { + string paramName = name + " ("; + for (int i = 0; i < param.Length; i++) + { + paramName += param[i].ParameterType.Name; + if (i < param.Length - 1) + paramName += ", "; + } + paramName += ")"; + + return paramName; + } + + private MethodInfo GetMethodInfo() + { + if (gameObject == null || component == null || string.IsNullOrEmpty(method)) + return null; + + Type[] types = new Type[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + types[i] = parameters[i].Type; + + return component.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, types, null); + } + + [Inspect, Collection(0, false), SerializeField] + private BindingParameter[] parameters = new BindingParameter[0]; + + public event ActionEventHandler OnInvoke; + + /// <summary> + /// Parameter-less method constructor. + /// </summary> + public ActionBinding() { } + + /// <summary> + /// The array of types is the method definition. + /// Method with the wrong parameters are not selectable. + /// </summary> + public ActionBinding(Type[] types) + { + internalParameters = new string[types.Length]; + for (int i = 0; i < types.Length; i++) + internalParameters[i] = types[i].AssemblyQualifiedName; + } + + /// <summary> + /// Invoke the method. + /// Be careful to pass the proper type as args, otherwise they will be ignored. + /// Args are only retained if the parameter is flagged as internal. + /// </summary> + public void Invoke(params object[] args) + { + if (gameObject == null || component == null || string.IsNullOrEmpty(method)) + return; + + MethodInfo info = GetMethodInfo(); + if (info == null) + return; + + object[] values = new object[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + if (args.Length > i && parameters[i].binding == BindingParameter.BindingType.Internal) + values[i] = args[i]; + else + values[i] = parameters[i].Value; + } + + info.Invoke(component, values); + + if (OnInvoke != null) + OnInvoke(this, values); + } + + public override string ToString() + { + string supplied = "("; + for (int i = 0; i < internalParameters.Length; i++) + { + supplied += Type.GetType(internalParameters[i]).Name; + if (i < internalParameters.Length - 1) + supplied += ", "; + } + + supplied += ")"; + + if (component != null) + return component.GetType().Name + " : " + method + " : " + supplied; + else + return supplied; + } + + public bool Copiable(object destination) + { + ActionBinding action = destination as ActionBinding; + if (action == null) + return false; + + if (action.internalParameters.Length != internalParameters.Length) + return false; + + for (int i = 0; i < internalParameters.Length; i++) + if (internalParameters[i] != action.internalParameters[i]) + return false; + + return true; + } + + /// <summary> + /// The binding parameter define how each of the argument of the invoked method is handled. + /// </summary> + [Serializable, AdvancedInspector] + public class BindingParameter : ICopy, ICopiable + { + [Inspect(-1), Restrict("RestrictBinding")] + public BindingType binding; + + private IList RestrictBinding() + { + List<object> list = new List<object>(); + if (canBeInternal) + list.Add(BindingType.Internal); + + list.Add(BindingType.Static); + list.Add(BindingType.External); + return list; + } + + private bool canBeInternal = true; + + private bool CanBeInternal + { + get { return canBeInternal; } + } + + #region Values + [SerializeField] + private BindingValueType type; + + [SerializeField] + private string qualifiedTypeName; + + public Type Type + { + get + { + if (string.IsNullOrEmpty(qualifiedTypeName)) + return null; + else + return Type.GetType(qualifiedTypeName); + } + + set + { + if (value == typeof(bool)) + type = BindingValueType.Boolean; + else if (value == typeof(Bounds)) + type = BindingValueType.Bounds; + else if (value == typeof(Color)) + type = BindingValueType.Color; + else if (value == typeof(float)) + type = BindingValueType.Float; + else if (value == typeof(int)) + type = BindingValueType.Integer; + else if (value == typeof(Rect)) + type = BindingValueType.Rect; + else if (typeof(UnityEngine.Object).IsAssignableFrom(value)) + type = BindingValueType.Reference; + else if (value == typeof(string)) + type = BindingValueType.String; + else if (value == typeof(Vector2)) + type = BindingValueType.Vector2; + else if (value == typeof(Vector3)) + type = BindingValueType.Vector3; + else if (value == typeof(Vector4)) + type = BindingValueType.Vector4; + else + type = BindingValueType.None; + + if (type != BindingValueType.None) + qualifiedTypeName = value.AssemblyQualifiedName; + else + qualifiedTypeName = ""; + } + } + + [Inspect(-2)] + public string BoundType + { + get + { + if (Type == null) + return ""; + + return Type.Name; + } + } + + [SerializeField] + private bool boolValue = false; + [SerializeField] + private int intValue = 0; + [SerializeField] + private float floatValue = 0; + [SerializeField] + private string stringValue = ""; + [SerializeField] + private Vector2 vector2Value = Vector2.zero; + [SerializeField] + private Vector3 vector3Value = Vector3.zero; + [SerializeField] + private Vector4 vector4Value = Vector4.zero; + [SerializeField] + private Color colorValue = Color.black; + [SerializeField] + private Rect rectValue = new Rect(0, 0, 0, 0); + [SerializeField] + private Bounds boundsValue = new Bounds(Vector3.zero, Vector3.zero); + [SerializeField] + private UnityEngine.Object referenceValue = new UnityEngine.Object(); + #endregion + + [Inspect("IsStatic")] + [RuntimeResolve("GetRuntimeType")] + public object Value + { + get + { + if (binding == BindingType.External) + { + object value = Invoke(); + if (value.GetType().IsAssignableFrom(Type)) + return value; + + System.ComponentModel.TypeConverter converter = System.ComponentModel.TypeDescriptor.GetConverter(Type); + return converter.ConvertTo(value, Type); + } + + switch (type) + { + case BindingValueType.Boolean: + return boolValue; + case BindingValueType.Bounds: + return boundsValue; + case BindingValueType.Color: + return colorValue; + case BindingValueType.Float: + return floatValue; + case BindingValueType.Integer: + return intValue; + case BindingValueType.Rect: + return rectValue; + case BindingValueType.Reference: + return referenceValue; + case BindingValueType.String: + return stringValue; + case BindingValueType.Vector2: + return vector2Value; + case BindingValueType.Vector3: + return vector3Value; + case BindingValueType.Vector4: + return vector4Value; + default: + return null; + } + } + + set + { + if (value == null && type != BindingValueType.Reference) + return; + + switch (type) + { + case BindingValueType.Boolean: + boolValue = (bool)value; + break; + case BindingValueType.Bounds: + boundsValue = (Bounds)value; + break; + case BindingValueType.Color: + colorValue = (Color)value; + break; + case BindingValueType.Float: + floatValue = (float)value; + break; + case BindingValueType.Integer: + intValue = (int)value; + break; + case BindingValueType.Rect: + rectValue = (Rect)value; + break; + case BindingValueType.Reference: + referenceValue = (UnityEngine.Object)value; + break; + case BindingValueType.String: + stringValue = (string)value; + break; + case BindingValueType.Vector2: + vector2Value = (Vector2)value; + break; + case BindingValueType.Vector3: + vector3Value = (Vector3)value; + break; + case BindingValueType.Vector4: + vector4Value = (Vector4)value; + break; + default: + return; + } + } + } + + [SerializeField] + private GameObject gameObject; + + [Inspect("IsExternal")] + public GameObject GameObject + { + get { return gameObject; } + set + { + if (gameObject != value) + { + gameObject = value; + Component = null; + } + } + } + + [SerializeField] + private Component component; + + [Inspect("IsExternal")] + [Restrict("GetComponents")] + public Component Component + { + get { return component; } + set + { + if (component != value) + { + component = value; + Method = null; + } + } + } + + private IList GetComponents() + { + List<DescriptorPair> components = new List<DescriptorPair>(); + if (gameObject == null) + return components; + + foreach (Component component in gameObject.GetComponents(typeof(Component))) + components.Add(new DescriptorPair(component, new DescriptorAttribute(component.GetType().Name, ""))); + + return components; + } + + [SerializeField] + private string method; + + [Inspect("IsExternal")] + [Restrict("GetMethods", RestrictDisplay.Toolbox)] + public MethodInfo Method + { + get { return GetMethodInfo(); } + set + { + if (value == null) + { + method = ""; + return; + } + + MethodInfo info = GetMethodInfo(); + if (info != value) + method = value.Name; + } + } + + private IList GetMethods() + { + List<DescriptorPair> methods = new List<DescriptorPair>(); + if (gameObject == null || component == null) + return methods; + + foreach (MethodInfo info in component.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) + { + if (info.IsGenericMethod || info.IsConstructor || info.IsFinal || info.IsSpecialName) + continue; + + if (!IsMethodValid(info)) + continue; + + string paramName = info.ReturnType.Name + " " + info.Name + "()"; + methods.Add(new DescriptorPair(info, new DescriptorAttribute(paramName, ""))); + } + + foreach (PropertyInfo info in component.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) + { + if (!info.CanRead) + continue; + + MethodInfo method = info.GetGetMethod(); + if (method == null || !IsMethodValid(method)) + continue; + + string paramName = method.ReturnType.Name + " " + info.Name + "()"; + methods.Add(new DescriptorPair(method, new DescriptorAttribute(paramName, ""))); + } + + return methods; + } + + private bool IsMethodValid(MethodInfo info) + { + ParameterInfo[] param = info.GetParameters(); + if (param.Length > 0) + return false; + + if (info.ReturnType == null || info.ReturnType == typeof(void)) + return false; + + System.ComponentModel.TypeConverter converter = System.ComponentModel.TypeDescriptor.GetConverter(info.ReturnType); + if (!Type.IsAssignableFrom(info.ReturnType) && !converter.CanConvertTo(Type)) + return false; + + return true; + } + + private MethodInfo GetMethodInfo() + { + if (gameObject == null || component == null || string.IsNullOrEmpty(method)) + return null; + + return component.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); + } + + private bool IsStatic() + { + return binding == BindingType.Static; + } + + private bool IsExternal() + { + return binding == BindingType.External; + } + + private Type GetRuntimeType() + { + return Type; + } + + public BindingParameter() { } + + public BindingParameter(bool canBeInternal) + { + this.canBeInternal = canBeInternal; + } + + public static bool IsValidType(Type type) + { + if (type == typeof(bool)) + return true; + else if (type == typeof(Bounds)) + return true; + else if (type == typeof(Color)) + return true; + else if (type == typeof(float)) + return true; + else if (type == typeof(int)) + return true; + else if (type == typeof(Rect)) + return true; + else if (typeof(UnityEngine.Object).IsAssignableFrom(type)) + return true; + else if (type == typeof(string)) + return true; + else if (type == typeof(Vector2)) + return true; + else if (type == typeof(Vector3)) + return true; + else if (type == typeof(Vector4)) + return true; + + return false; + } + + public enum BindingType + { + Internal, + Static, + External + } + + private enum BindingValueType + { + None, + + Boolean, + Integer, + Float, + String, + Vector2, + Vector3, + Vector4, + Color, + Rect, + Bounds, + Reference + } + + private object Invoke() + { + if (gameObject == null || component == null || string.IsNullOrEmpty(method)) + return null; + + MethodInfo info = GetMethodInfo(); + if (info == null) + return null; + + return info.Invoke(component, new object[0]); + } + + public bool Copiable(object destination) + { + BindingParameter target = destination as BindingParameter; + if (target == null) + return false; + + if (target.type != type) + return false; + + if (!target.canBeInternal && binding == BindingType.Internal) + return false; + + return true; + } + + public object Copy(object destination) + { + BindingParameter target = destination as BindingParameter; + + BindingParameter copy = new BindingParameter(); + if (target != null) + copy.canBeInternal = target.canBeInternal; + else + copy.canBeInternal = canBeInternal; + + copy.binding = binding; + copy.boolValue = boolValue; + copy.boundsValue = boundsValue; + copy.colorValue = colorValue; + copy.component = component; + copy.floatValue = floatValue; + copy.gameObject = gameObject; + copy.intValue = intValue; + copy.method = method; + copy.qualifiedTypeName = qualifiedTypeName; + copy.rectValue = rectValue; + copy.referenceValue = referenceValue; + copy.stringValue = stringValue; + copy.type = type; + copy.vector2Value = vector2Value; + copy.vector3Value = vector3Value; + copy.vector4Value = vector4Value; + + return copy; + } + + public override string ToString() + { + return type.ToString(); + } + } + } +}
\ No newline at end of file |