using System; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using System.Reflection; using UnityEngine.UIElements; using UnityEditor.UIElements; using System.Collections.Generic; using EditorAttributes.Editor.Utility; using Object = UnityEngine.Object; namespace EditorAttributes.Editor { [CanEditMultipleObjects, CustomEditor(typeof(Object), true)] public class EditorExtension : UnityEditor.Editor { public static readonly Color DEFAULT_GLOBAL_COLOR = new(0.8f, 0.8f, 0.8f, 1.0f); public static Color GLOBAL_COLOR = DEFAULT_GLOBAL_COLOR; private string buttonParamsDataFilePath; private Dictionary buttonFoldouts = new(); private Dictionary buttonParameterValues = new(); private MethodInfo[] functions; protected virtual void OnEnable() { var funcList = new List(); var targetType = target.GetType(); while (targetType != null) { funcList.AddRange(targetType.GetMethods(ReflectionUtility.BINDING_FLAGS)); targetType = targetType.BaseType; } functions = funcList.ToArray(); ButtonDrawer.LoadParamsData(functions, target, ref buttonFoldouts, ref buttonParameterValues); try { buttonParamsDataFilePath = Path.Combine(ButtonDrawer.PARAMS_DATA_LOCATION, ButtonDrawer.GetFileName(target)); } catch (ArgumentException) { return; } } protected virtual void OnDisable() { if (target == null) ButtonDrawer.DeleteParamsData(buttonParamsDataFilePath); EditorHandles.handleProperties.Clear(); EditorHandles.boundsHandleList.Clear(); } void OnSceneGUI() => EditorHandles.DrawHandles(); public override VisualElement CreateInspectorGUI() { // Reset the global color per component GUI so it doesnt leak from other components GLOBAL_COLOR = DEFAULT_GLOBAL_COLOR; var root = new VisualElement(); var nonSerializedMembers = DrawNonSerializedMembers(); var defaultInspector = DrawDefaultInspector(); var buttons = DrawButtons(); root.Add(defaultInspector); root.Add(nonSerializedMembers); root.Add(buttons); return root; } protected virtual new VisualElement DrawDefaultInspector() { var root = new VisualElement(); var propertyList = new Dictionary(); using (var property = serializedObject.GetIterator()) { if (property.NextVisible(true)) { IColorAttribute prevColor = null; do { var propertyField = PropertyDrawerBase.CreatePropertyField(property); if (property.name == "m_Script") { propertyField.SetEnabled(false); root.Add(propertyField); continue; } var field = ReflectionUtility.FindField(property.name, target); if (field?.GetCustomAttribute() != null) propertyField.style.display = DisplayStyle.None; var colorAttribute = field?.GetCustomAttribute(); if (colorAttribute != null) { GUIColorDrawer.ColorField(propertyField, colorAttribute); prevColor = colorAttribute; } else if (prevColor != null) { GUIColorDrawer.ColorField(propertyField, prevColor); } propertyList.Add(property.name, propertyField); } while (property.NextVisible(false)); } } var orderedProperties = propertyList.OrderBy((property) => { var field = ReflectionUtility.FindField(property.Key, target); var propertyOrderAttribute = field?.GetCustomAttribute(); if (propertyOrderAttribute != null) return propertyOrderAttribute.PropertyOrder; return 0; }); foreach (var property in orderedProperties) root.Add(property.Value); return root; } /// /// Draws all the members marked with the ShowInInspector attribute /// /// A visual element containing all non serialized member fields protected VisualElement DrawNonSerializedMembers() { var root = new VisualElement(); var nonSerializedFields = target.GetType().GetFields(ReflectionUtility.BINDING_FLAGS).Where((field) => field.GetCustomAttribute() != null); foreach (var nonSerializedField in nonSerializedFields) { if (HasRestrictedAttributes(nonSerializedField, out string errorMessage)) { root.Add(new HelpBox(errorMessage, HelpBoxMessageType.Error)); continue; } var field = DrawNonSerializedField(nonSerializedField, nonSerializedField.FieldType, nonSerializedField.GetValue(target)); root.Add(field); } var nonSerializedProperties = target.GetType().GetProperties(ReflectionUtility.BINDING_FLAGS).Where((field) => field.GetCustomAttribute() != null); foreach (var nonSerializedProperty in nonSerializedProperties) { if (HasRestrictedAttributes(nonSerializedProperty, out string errorMessage)) { root.Add(new HelpBox(errorMessage, HelpBoxMessageType.Error)); continue; } var field = DrawNonSerializedField(nonSerializedProperty, nonSerializedProperty.PropertyType, nonSerializedProperty.GetValue(target)); root.Add(field); } var nonSerializedMethods = target.GetType().GetMethods(ReflectionUtility.BINDING_FLAGS).Where((field) => field.GetCustomAttribute() != null); foreach (var nonSerializedMethod in nonSerializedMethods) { if (HasRestrictedAttributes(nonSerializedMethod, out string errorMessage)) { root.Add(new HelpBox(errorMessage, HelpBoxMessageType.Error)); continue; } if (nonSerializedMethod.GetParameters().Length > 0 || nonSerializedMethod.ContainsGenericParameters) { root.Add(new HelpBox($"Method {nonSerializedMethod.Name} cannot be drawn because it has parameters or is generic", HelpBoxMessageType.Error)); continue; } var field = DrawNonSerializedField(nonSerializedMethod, nonSerializedMethod.ReturnType, nonSerializedMethod.Invoke(target, null)); root.Add(field); } return root; } private VisualElement DrawNonSerializedField(MemberInfo memberInfo, Type memberType, object memberValue) { var root = new VisualElement(); var header = new Label() { style = { marginTop = 13, marginLeft = 3, marginRight = -2, unityFontStyleAndWeight = FontStyle.Bold, unityTextAlign = TextAnchor.LowerLeft } }; header.AddToClassList("unity-header-drawer__label"); var field = PropertyDrawerBase.CreateFieldForType(memberType, memberInfo.Name, memberValue, AreNonSerializedMemberValuesDifferent(memberInfo, targets)); field.AddToClassList(BaseField.alignedFieldUssClassName); if (field is Foldout) { field.contentContainer.SetEnabled(false); field.Q