using UnityEngine; using UnityEditor; using System; using System.Reflection; using System.Linq; using System.Collections.Generic; namespace MoreMountains.Tools { public class MMInspectorGroupData { public bool GroupIsOpen; public MMInspectorGroupAttribute GroupAttribute; public List PropertiesList = new List(); public HashSet GroupHashSet = new HashSet(); public Color GroupColor; public void ClearGroup() { GroupAttribute = null; GroupHashSet.Clear(); PropertiesList.Clear(); } } /// /// A generic drawer for all MMMonoBehaviour, handles both the Group and RequiresConstantRepaint attributes /// [CanEditMultipleObjects] [CustomEditor(typeof(MMMonoBehaviour), true, isFallback = true)] public class MMMonoBehaviourDrawer : UnityEditor.Editor { public bool DrawerInitialized; public List PropertiesList = new List(); public Dictionary GroupData = new Dictionary(); private string[] _mmHiddenPropertiesToHide; private bool _hasMMHiddenProperties = false; private bool _requiresConstantRepaint; protected bool _shouldDrawBase = true; public override bool RequiresConstantRepaint() { return _requiresConstantRepaint; } protected virtual void OnEnable() { DrawerInitialized = false; if (!target || !serializedObject.targetObject) { return; } _requiresConstantRepaint = serializedObject.targetObject.GetType().GetCustomAttribute() != null; MMHiddenPropertiesAttribute[] hiddenProperties = (MMHiddenPropertiesAttribute[])target.GetType().GetCustomAttributes(typeof(MMHiddenPropertiesAttribute), false); if (hiddenProperties != null && hiddenProperties.Length > 0 && hiddenProperties[0].PropertiesNames != null) { _mmHiddenPropertiesToHide = hiddenProperties[0].PropertiesNames; _hasMMHiddenProperties = true; } } protected virtual void OnDisable() { if (target == null) { return; } foreach (KeyValuePair groupData in GroupData) { EditorPrefs.SetBool(string.Format($"{groupData.Value.GroupAttribute.GroupName}{groupData.Value.PropertiesList[0].name}{target.GetInstanceID()}"), groupData.Value.GroupIsOpen); groupData.Value.ClearGroup(); } } public override void OnInspectorGUI() { serializedObject.Update(); Initialization(); DrawBase(); DrawScriptBox(); DrawContainer(); DrawContents(); serializedObject.ApplyModifiedProperties(); } protected virtual void Initialization() { if (DrawerInitialized) { return; } List fieldInfoList; MMInspectorGroupAttribute previousGroupAttribute = default; int fieldInfoLength = MMMonoBehaviourFieldInfo.GetFieldInfo(target, out fieldInfoList); for (int i = 0; i < fieldInfoLength; i++) { MMInspectorGroupAttribute group = Attribute.GetCustomAttribute(fieldInfoList[i], typeof(MMInspectorGroupAttribute)) as MMInspectorGroupAttribute; MMInspectorGroupData groupData; if (group == null) { if (previousGroupAttribute != null && previousGroupAttribute.GroupAllFieldsUntilNextGroupAttribute) { _shouldDrawBase = false; if (!GroupData.TryGetValue(previousGroupAttribute.GroupName, out groupData)) { GroupData.Add(previousGroupAttribute.GroupName, new MMInspectorGroupData { GroupAttribute = previousGroupAttribute, GroupHashSet = new HashSet { fieldInfoList[i].Name }, GroupColor = MMColors.GetColorAt(previousGroupAttribute.GroupColorIndex) }); } else { groupData.GroupColor = MMColors.GetColorAt(previousGroupAttribute.GroupColorIndex); groupData.GroupHashSet.Add(fieldInfoList[i].Name); } } continue; } previousGroupAttribute = group; if (!GroupData.TryGetValue(group.GroupName, out groupData)) { bool groupIsOpen = EditorPrefs.GetBool(string.Format($"{group.GroupName}{fieldInfoList[i].Name}{target.GetInstanceID()}"), false); GroupData.Add(group.GroupName, new MMInspectorGroupData { GroupAttribute = group, GroupColor = MMColors.GetColorAt(previousGroupAttribute.GroupColorIndex), GroupHashSet = new HashSet { fieldInfoList[i].Name }, GroupIsOpen = groupIsOpen }); } else { groupData.GroupHashSet.Add(fieldInfoList[i].Name); groupData.GroupColor = MMColors.GetColorAt(previousGroupAttribute.GroupColorIndex); } } SerializedProperty iterator = serializedObject.GetIterator(); if (iterator.NextVisible(true)) { do { FillPropertiesList(iterator); } while (iterator.NextVisible(false)); } DrawerInitialized = true; } protected virtual void DrawBase() { if (_shouldDrawBase) { DrawDefaultInspector(); return; } } protected virtual void DrawScriptBox() { if (PropertiesList.Count == 0) { return; } using (new EditorGUI.DisabledScope("m_Script" == PropertiesList[0].propertyPath)) { EditorGUILayout.PropertyField(PropertiesList[0], true); } } protected virtual void DrawContainer() { if (PropertiesList.Count == 0) { return; } foreach (KeyValuePair pair in GroupData) { this.DrawVerticalLayout(() => DrawGroup(pair.Value), MMMonoBehaviourDrawerStyle.ContainerStyle); EditorGUI.indentLevel = 0; } } protected virtual void DrawContents() { if (PropertiesList.Count == 0) { return; } EditorGUILayout.Space(); for (int i = 1; i < PropertiesList.Count; i++) { if (_hasMMHiddenProperties && (!_mmHiddenPropertiesToHide.Contains(PropertiesList[i].name))) { EditorGUILayout.PropertyField(PropertiesList[i], true); } } } protected virtual void DrawGroup(MMInspectorGroupData groupData) { Rect verticalGroup = EditorGUILayout.BeginVertical(); var leftBorderRect = new Rect(verticalGroup.xMin + 5, verticalGroup.yMin - 10, 3f, verticalGroup.height + 20); leftBorderRect.xMin = 15f; leftBorderRect.xMax = 18f; EditorGUI.DrawRect(leftBorderRect, groupData.GroupColor); groupData.GroupIsOpen = EditorGUILayout.Foldout(groupData.GroupIsOpen, groupData.GroupAttribute.GroupName, true, MMMonoBehaviourDrawerStyle.GroupStyle); if (groupData.GroupIsOpen) { EditorGUI.indentLevel = 0; for (int i = 0; i < groupData.PropertiesList.Count; i++) { this.DrawVerticalLayout(() => DrawChild(i), MMMonoBehaviourDrawerStyle.BoxChildStyle); } } EditorGUILayout.EndVertical(); void DrawChild(int i) { if ((_hasMMHiddenProperties) && (_mmHiddenPropertiesToHide.Contains(groupData.PropertiesList[i].name))) { return; } EditorGUILayout.PropertyField(groupData.PropertiesList[i], new GUIContent(ObjectNames.NicifyVariableName(groupData.PropertiesList[i].name)), true); } } public void FillPropertiesList(SerializedProperty serializedProperty) { bool shouldClose = false; foreach (KeyValuePair pair in GroupData) { if (pair.Value.GroupHashSet.Contains(serializedProperty.name)) { SerializedProperty property = serializedProperty.Copy(); shouldClose = true; pair.Value.PropertiesList.Add(property); break; } } if (!shouldClose) { SerializedProperty property = serializedProperty.Copy(); PropertiesList.Add(property); } } } }