A game about forced loneliness, made by TACStudios
at master 242 lines 11 kB view raw
1using System; 2using UnityEditor; 3using UnityEngine; 4 5namespace Unity.Mathematics.Editor 6{ 7 [CustomPropertyDrawer(typeof(bool2)), CustomPropertyDrawer(typeof(bool3)), CustomPropertyDrawer(typeof(bool4))] 8 [CustomPropertyDrawer(typeof(double2)), CustomPropertyDrawer(typeof(double3)), CustomPropertyDrawer(typeof(double4))] 9 [CustomPropertyDrawer(typeof(float2)), CustomPropertyDrawer(typeof(float3)), CustomPropertyDrawer(typeof(float4))] 10 [CustomPropertyDrawer(typeof(int2)), CustomPropertyDrawer(typeof(int3)), CustomPropertyDrawer(typeof(int4))] 11 [CustomPropertyDrawer(typeof(uint2)), CustomPropertyDrawer(typeof(uint3)), CustomPropertyDrawer(typeof(uint4))] 12 [CustomPropertyDrawer(typeof(DoNotNormalizeAttribute))] 13 class PrimitiveVectorDrawer : PropertyDrawer 14 { 15 private string _PropertyType; 16 17 string GetPropertyType(SerializedProperty property) 18 { 19 if (_PropertyType == null) 20 { 21 _PropertyType = property.type; 22 var isManagedRef = property.type.StartsWith("managedReference", StringComparison.Ordinal); 23 if (isManagedRef) 24 { 25 var startIndex = "managedReference<".Length; 26 var length = _PropertyType.Length - startIndex - 1; 27 _PropertyType = _PropertyType.Substring("managedReference<".Length, length); 28 } 29 } 30 31 return _PropertyType; 32 } 33 34 static class Content 35 { 36 public static readonly string doNotNormalizeCompatibility = L10n.Tr( 37 $"{typeof(DoNotNormalizeAttribute).Name} only works with {typeof(quaternion)} and primitive vector types." 38 ); 39 public static readonly string doNotNormalizeTooltip = 40 L10n.Tr("This value is not normalized, which may produce unexpected results."); 41 42 public static readonly GUIContent[] labels2 = { new GUIContent("X"), new GUIContent("Y") }; 43 public static readonly GUIContent[] labels3 = { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z") }; 44 public static readonly GUIContent[] labels4 = { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z"), new GUIContent("W") }; 45 } 46 47#if !UNITY_2023_2_OR_NEWER 48 public override bool CanCacheInspectorGUI(SerializedProperty property) 49 { 50 return false; 51 } 52#endif 53 54 public override float GetPropertyHeight(SerializedProperty property, GUIContent label) 55 { 56 var height = EditorGUIUtility.singleLineHeight; 57 if (!EditorGUIUtility.wideMode) 58 height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; 59 return height; 60 } 61 62 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 63 { 64 var subLabels = Content.labels4; 65 var startIter = "x"; 66 var propertyType = GetPropertyType(property); 67 switch (propertyType[propertyType.Length - 1]) 68 { 69 case '2': 70 subLabels = Content.labels2; 71 break; 72 case '3': 73 subLabels = Content.labels3; 74 break; 75 case '4': 76 subLabels = Content.labels4; 77 break; 78 default: 79 { 80 if (property.type == nameof(quaternion)) 81 startIter = "value.x"; 82 else if (attribute is DoNotNormalizeAttribute) 83 { 84 EditorGUI.HelpBox(EditorGUI.PrefixLabel(position, label), Content.doNotNormalizeCompatibility, MessageType.None); 85 return; 86 } 87 break; 88 } 89 } 90 91 if (attribute is DoNotNormalizeAttribute && string.IsNullOrEmpty(label.tooltip)) 92 label.tooltip = Content.doNotNormalizeTooltip; 93 94 label = EditorGUI.BeginProperty(position, label, property); 95 var valuesIterator = property.FindPropertyRelative(startIter); 96 MultiPropertyField(position, subLabels, valuesIterator, label); 97 EditorGUI.EndProperty(); 98 } 99 100 void MultiPropertyField(Rect position, GUIContent[] subLabels, SerializedProperty valuesIterator, GUIContent label) 101 { 102#if UNITY_2022_1_OR_NEWER 103 EditorGUI.MultiPropertyField(position, subLabels, valuesIterator, label, EditorGUI.PropertyVisibility.All); 104#else 105 EditorGUICopy.MultiPropertyField(position, subLabels, valuesIterator, label); 106#endif 107 } 108 } 109 110#if !UNITY_2022_1_OR_NEWER 111 internal class EditorGUICopy 112 { 113 internal const float kSpacingSubLabel = 4; 114 private const float kIndentPerLevel = 15; 115 internal const float kPrefixPaddingRight = 2; 116 internal static int indentLevel = 0; 117 private static readonly int s_FoldoutHash = "Foldout".GetHashCode(); 118 119 // internal static readonly SVC<float> kVerticalSpacingMultiField = new SVC<float>("--theme-multifield-vertical-spacing", 0.0f); 120 // kVerticalSpacingMultiField should actually look like the above line ^^^ but we don't have access to SVC<T>, 121 // so instead we just set this value to what is observed in the debugger with the Unity dark theme. 122 internal const float kVerticalSpacingMultiField = 2; 123 124 internal enum PropertyVisibility 125 { 126 All, 127 OnlyVisible 128 } 129 130 // This code is basically EditorGUI.MultiPropertyField(Rect, GUIContent[], SerializedProperty, GUIContent), 131 // but with the property visibility assumed to be "All" instead of "OnlyVisible". We really want to have "All" 132 // because it's possible for someone to hide something in the inspector with [HideInInspector] but then manually 133 // draw it themselves later. In this case, if you called EditorGUI.MultiPropertyField() directly, you'd 134 // end up with some fields that point to some unrelated visible property. 135 public static void MultiPropertyField(Rect position, GUIContent[] subLabels, SerializedProperty valuesIterator, GUIContent label) 136 { 137 int id = GUIUtility.GetControlID(s_FoldoutHash, FocusType.Keyboard, position); 138 position = MultiFieldPrefixLabel(position, id, label, subLabels.Length); 139 position.height = EditorGUIUtility.singleLineHeight; 140 MultiPropertyFieldInternal(position, subLabels, valuesIterator, PropertyVisibility.All); 141 } 142 143 internal static void BeginDisabled(bool disabled) 144 { 145 // Unused, but left here to minimize changes in EditorGUICopy.MultiPropertyFieldInternal(). 146 } 147 148 internal static void EndDisabled() 149 { 150 // Unused, but left here to minimize changes in EditorGUICopy.MultiPropertyFieldInternal(). 151 } 152 153 internal static float CalcPrefixLabelWidth(GUIContent label, GUIStyle style = null) 154 { 155 if (style == null) 156 style = EditorStyles.label; 157 return style.CalcSize(label).x; 158 } 159 160 internal static void MultiPropertyFieldInternal(Rect position, GUIContent[] subLabels, SerializedProperty valuesIterator, PropertyVisibility visibility, bool[] disabledMask = null, float prefixLabelWidth = -1) 161 { 162 int eCount = subLabels.Length; 163 float w = (position.width - (eCount - 1) * kSpacingSubLabel) / eCount; 164 Rect nr = new Rect(position) {width = w}; 165 float t = EditorGUIUtility.labelWidth; 166 int l = indentLevel; 167 indentLevel = 0; 168 for (int i = 0; i < subLabels.Length; i++) 169 { 170 EditorGUIUtility.labelWidth = prefixLabelWidth > 0 ? prefixLabelWidth : CalcPrefixLabelWidth(subLabels[i]); 171 172 if (disabledMask != null) 173 BeginDisabled(disabledMask[i]); 174 EditorGUI.PropertyField(nr, valuesIterator, subLabels[i]); 175 if (disabledMask != null) 176 EndDisabled(); 177 nr.x += w + kSpacingSubLabel; 178 179 switch (visibility) 180 { 181 case PropertyVisibility.All: 182 valuesIterator.Next(false); 183 break; 184 185 case PropertyVisibility.OnlyVisible: 186 valuesIterator.NextVisible(false); 187 break; 188 } 189 } 190 EditorGUIUtility.labelWidth = t; 191 indentLevel = l; 192 } 193 194 internal static bool LabelHasContent(GUIContent label) 195 { 196 if (label == null) 197 { 198 return true; 199 } 200 // @TODO: find out why checking for GUIContent.none doesn't work 201 return label.text != string.Empty || label.image != null; 202 } 203 204 internal static float indent => indentLevel * kIndentPerLevel; 205 206 internal static Rect MultiFieldPrefixLabel(Rect totalPosition, int id, GUIContent label, int columns) 207 { 208 if (!LabelHasContent(label)) 209 { 210 return EditorGUI.IndentedRect(totalPosition); 211 } 212 213 if (EditorGUIUtility.wideMode) 214 { 215 Rect labelPosition = new Rect(totalPosition.x + indent, totalPosition.y, EditorGUIUtility.labelWidth - indent, EditorGUIUtility.singleLineHeight); 216 Rect fieldPosition = totalPosition; 217 fieldPosition.xMin += EditorGUIUtility.labelWidth + kPrefixPaddingRight; 218 219 // If there are 2 columns we use the same column widths as if there had been 3 columns 220 // in order to make columns line up neatly. 221 if (columns == 2) 222 { 223 float columnWidth = (fieldPosition.width - (3 - 1) * kSpacingSubLabel) / 3f; 224 fieldPosition.xMax -= (columnWidth + kSpacingSubLabel); 225 } 226 227 EditorGUI.HandlePrefixLabel(totalPosition, labelPosition, label, id); 228 return fieldPosition; 229 } 230 else 231 { 232 Rect labelPosition = new Rect(totalPosition.x + indent, totalPosition.y, totalPosition.width - indent, EditorGUIUtility.singleLineHeight); 233 Rect fieldPosition = totalPosition; 234 fieldPosition.xMin += indent + kIndentPerLevel; 235 fieldPosition.yMin += EditorGUIUtility.singleLineHeight + kVerticalSpacingMultiField; 236 EditorGUI.HandlePrefixLabel(totalPosition, labelPosition, label, id); 237 return fieldPosition; 238 } 239 } 240 } 241#endif 242}