// ---------------------------------------------------------------------------- // Author: Kaynn, Yeo Wen Qin // https://github.com/Kaynn-Cahya // Date: 26/02/2019 // ---------------------------------------------------------------------------- using System; using Jovian.Utilities; using UnityEngine; using Object = UnityEngine.Object; namespace InspectorToolkit { [AttributeUsage(AttributeTargets.Method)] public class ButtonMethodAttribute : PropertyAttribute { public readonly ButtonMethodDrawOrder DrawOrder; public readonly PlayModeVisibility Visibility; public ButtonMethodAttribute(PlayModeVisibility visibility, ButtonMethodDrawOrder drawOrder = ButtonMethodDrawOrder.AfterInspector) { Visibility = visibility; DrawOrder = drawOrder; } public ButtonMethodAttribute(ButtonMethodDrawOrder drawOrder = ButtonMethodDrawOrder.AfterInspector) { DrawOrder = drawOrder; Visibility = PlayModeVisibility.Always; } } public enum ButtonMethodDrawOrder { BeforeInspector, AfterInspector } } #if UNITY_EDITOR namespace InspectorToolkit.Internal { using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; public class ButtonMethodHandler { public class MethodData { public MethodInfo methodInfo; public ParameterData[] parameterDatas; public string niceName; public PlayModeVisibility visibility; public ButtonMethodDrawOrder order; } public class ParameterData { public ParameterInfo parameterInfo; public string niceName; public object value; } // TODO - replace with MethodDatas; public readonly List<(MethodInfo Method, string Name, PlayModeVisibility visibility, ButtonMethodDrawOrder order)> TargetMethods; public int Amount => TargetMethods?.Count ?? 0; public readonly List MethodDatas; public bool HasMethods => MethodDatas?.Count > 0; private readonly Object _target; public Object Target => _target; public ButtonMethodHandler(Object target) { _target = target; Type type = target.GetType(); BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; IEnumerable members = type.GetMembers(bindings).Where(IsButtonMethod); foreach(MemberInfo member in members) { MethodInfo method = member as MethodInfo; if(method == null) continue; if(IsValidMember(method, member)) { ButtonMethodAttribute attribute = (ButtonMethodAttribute)Attribute.GetCustomAttribute(method, typeof(ButtonMethodAttribute)); TargetMethods ??= new List<(MethodInfo, string, PlayModeVisibility, ButtonMethodDrawOrder)>(); TargetMethods.Add((method, ObjectNames.NicifyVariableName(method.Name), attribute.Visibility, attribute.DrawOrder)); MethodDatas ??= new(); ParameterInfo[] parameterInfos = method.GetParameters(); ParameterData[] parameterDatas = new ParameterData[parameterInfos.Length]; for(int i = 0; i < parameterInfos.Length; i++) { ParameterInfo info = parameterInfos[i]; object value; if(info.HasDefaultValue) { value = info.DefaultValue; } else if(info.ParameterType == typeof(string)) { value = "String"; } else if(info.ParameterType == typeof(Object) || info.ParameterType.IsSubclassOf(typeof(Object))) { value = null; } else { value = Activator.CreateInstance(info.ParameterType); } parameterDatas[i] = new ParameterData() { niceName = ObjectNames.NicifyVariableName(info.Name), parameterInfo = info, value = value }; } MethodData methodData = new MethodData() { methodInfo = method, niceName = ObjectNames.NicifyVariableName(method.Name), visibility = attribute.Visibility, order = attribute.DrawOrder, parameterDatas = parameterDatas }; MethodDatas.Add(methodData); } } } public bool HasAnyVisibleMethods() { if(MethodDatas == null || MethodDatas.Count == 0) return false; foreach (MethodData methodData in MethodDatas) { if (methodData.visibility.IsVisible()) { return true; } } return false; } public void OnBeforeInspectorGUI() { if(MethodDatas == null || MethodDatas.Count == 0) return; DrawButtonMethods(ButtonMethodDrawOrder.BeforeInspector); } private void DrawButtonMethods(ButtonMethodDrawOrder drawOrder) { foreach(MethodData method in MethodDatas) { if(method.order != drawOrder) continue; if(!method.visibility.IsVisible()) continue; if(method.parameterDatas.Length > 0) { GUILayout.BeginVertical(EditorStyles.helpBox); } if(GUILayout.Button(method.niceName)) { object result = method.methodInfo.Invoke(_target, method.parameterDatas.Select(p=>p.value).ToArray()); if(result != null) { Debug.Log($"{method.niceName} => {result}"); } } foreach (ParameterData p in method.parameterDatas) { p.value = InspectorGUIUtility.DrawField(p.niceName, p.parameterInfo.ParameterType, p.value); } if(method.parameterDatas.Length > 0) { GUILayout.EndVertical(); } } } public void OnAfterInspectorGUI() { if(MethodDatas == null || MethodDatas.Count == 0) return; DrawButtonMethods(ButtonMethodDrawOrder.AfterInspector); } public void Invoke(MethodInfo method) => InvokeMethod(_target, method); private void InvokeMethod(Object target, MethodInfo method) { object result = method.Invoke(target, null); if(result != null) { string message = $"{result} \nResult of Method '{method.Name}' invocation on object {target.name}"; Debug.Log(message, target); } } private bool IsButtonMethod(MemberInfo memberInfo) { return Attribute.IsDefined(memberInfo, typeof(ButtonMethodAttribute)); } private bool IsValidMember(MethodInfo method, MemberInfo member) { if(method == null) { Debug.LogWarning( $"Property {member.Name}.Reason: Member is not a method but has EditorButtonAttribute!"); return false; } return true; } } } #endif