No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PrimitiveVectorDrawer.cs 11KB

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