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.
240 lines
10 KiB
240 lines
10 KiB
using System; |
|
using UnityEditor; |
|
using UnityEngine; |
|
|
|
namespace Unity.Mathematics.Editor |
|
{ |
|
[CustomPropertyDrawer(typeof(bool2)), CustomPropertyDrawer(typeof(bool3)), CustomPropertyDrawer(typeof(bool4))] |
|
[CustomPropertyDrawer(typeof(double2)), CustomPropertyDrawer(typeof(double3)), CustomPropertyDrawer(typeof(double4))] |
|
[CustomPropertyDrawer(typeof(float2)), CustomPropertyDrawer(typeof(float3)), CustomPropertyDrawer(typeof(float4))] |
|
[CustomPropertyDrawer(typeof(int2)), CustomPropertyDrawer(typeof(int3)), CustomPropertyDrawer(typeof(int4))] |
|
[CustomPropertyDrawer(typeof(uint2)), CustomPropertyDrawer(typeof(uint3)), CustomPropertyDrawer(typeof(uint4))] |
|
[CustomPropertyDrawer(typeof(DoNotNormalizeAttribute))] |
|
class PrimitiveVectorDrawer : PropertyDrawer |
|
{ |
|
private string _PropertyType; |
|
|
|
string GetPropertyType(SerializedProperty property) |
|
{ |
|
if (_PropertyType == null) |
|
{ |
|
_PropertyType = property.type; |
|
var isManagedRef = property.type.StartsWith("managedReference", StringComparison.Ordinal); |
|
if (isManagedRef) |
|
{ |
|
var startIndex = "managedReference<".Length; |
|
var length = _PropertyType.Length - startIndex - 1; |
|
_PropertyType = _PropertyType.Substring("managedReference<".Length, length); |
|
} |
|
} |
|
|
|
return _PropertyType; |
|
} |
|
|
|
static class Content |
|
{ |
|
public static readonly string doNotNormalizeCompatibility = L10n.Tr( |
|
$"{typeof(DoNotNormalizeAttribute).Name} only works with {typeof(quaternion)} and primitive vector types." |
|
); |
|
public static readonly string doNotNormalizeTooltip = |
|
L10n.Tr("This value is not normalized, which may produce unexpected results."); |
|
|
|
public static readonly GUIContent[] labels2 = { new GUIContent("X"), new GUIContent("Y") }; |
|
public static readonly GUIContent[] labels3 = { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z") }; |
|
public static readonly GUIContent[] labels4 = { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z"), new GUIContent("W") }; |
|
} |
|
|
|
public override bool CanCacheInspectorGUI(SerializedProperty property) |
|
{ |
|
return false; |
|
} |
|
|
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) |
|
{ |
|
var height = EditorGUIUtility.singleLineHeight; |
|
if (!EditorGUIUtility.wideMode) |
|
height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; |
|
return height; |
|
} |
|
|
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) |
|
{ |
|
var subLabels = Content.labels4; |
|
var startIter = "x"; |
|
var propertyType = GetPropertyType(property); |
|
switch (propertyType[propertyType.Length - 1]) |
|
{ |
|
case '2': |
|
subLabels = Content.labels2; |
|
break; |
|
case '3': |
|
subLabels = Content.labels3; |
|
break; |
|
case '4': |
|
subLabels = Content.labels4; |
|
break; |
|
default: |
|
{ |
|
if (property.type == nameof(quaternion)) |
|
startIter = "value.x"; |
|
else if (attribute is DoNotNormalizeAttribute) |
|
{ |
|
EditorGUI.HelpBox(EditorGUI.PrefixLabel(position, label), Content.doNotNormalizeCompatibility, MessageType.None); |
|
return; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
if (attribute is DoNotNormalizeAttribute && string.IsNullOrEmpty(label.tooltip)) |
|
label.tooltip = Content.doNotNormalizeTooltip; |
|
|
|
label = EditorGUI.BeginProperty(position, label, property); |
|
var valuesIterator = property.FindPropertyRelative(startIter); |
|
MultiPropertyField(position, subLabels, valuesIterator, label); |
|
EditorGUI.EndProperty(); |
|
} |
|
|
|
void MultiPropertyField(Rect position, GUIContent[] subLabels, SerializedProperty valuesIterator, GUIContent label) |
|
{ |
|
#if UNITY_2022_1_OR_NEWER |
|
EditorGUI.MultiPropertyField(position, subLabels, valuesIterator, label, EditorGUI.PropertyVisibility.All); |
|
#else |
|
EditorGUICopy.MultiPropertyField(position, subLabels, valuesIterator, label); |
|
#endif |
|
} |
|
} |
|
|
|
#if !UNITY_2022_1_OR_NEWER |
|
internal class EditorGUICopy |
|
{ |
|
internal const float kSpacingSubLabel = 4; |
|
private const float kIndentPerLevel = 15; |
|
internal const float kPrefixPaddingRight = 2; |
|
internal static int indentLevel = 0; |
|
private static readonly int s_FoldoutHash = "Foldout".GetHashCode(); |
|
|
|
// internal static readonly SVC<float> kVerticalSpacingMultiField = new SVC<float>("--theme-multifield-vertical-spacing", 0.0f); |
|
// kVerticalSpacingMultiField should actually look like the above line ^^^ but we don't have access to SVC<T>, |
|
// so instead we just set this value to what is observed in the debugger with the Unity dark theme. |
|
internal const float kVerticalSpacingMultiField = 2; |
|
|
|
internal enum PropertyVisibility |
|
{ |
|
All, |
|
OnlyVisible |
|
} |
|
|
|
// This code is basically EditorGUI.MultiPropertyField(Rect, GUIContent[], SerializedProperty, GUIContent), |
|
// but with the property visibility assumed to be "All" instead of "OnlyVisible". We really want to have "All" |
|
// because it's possible for someone to hide something in the inspector with [HideInInspector] but then manually |
|
// draw it themselves later. In this case, if you called EditorGUI.MultiPropertyField() directly, you'd |
|
// end up with some fields that point to some unrelated visible property. |
|
public static void MultiPropertyField(Rect position, GUIContent[] subLabels, SerializedProperty valuesIterator, GUIContent label) |
|
{ |
|
int id = GUIUtility.GetControlID(s_FoldoutHash, FocusType.Keyboard, position); |
|
position = MultiFieldPrefixLabel(position, id, label, subLabels.Length); |
|
position.height = EditorGUIUtility.singleLineHeight; |
|
MultiPropertyFieldInternal(position, subLabels, valuesIterator, PropertyVisibility.All); |
|
} |
|
|
|
internal static void BeginDisabled(bool disabled) |
|
{ |
|
// Unused, but left here to minimize changes in EditorGUICopy.MultiPropertyFieldInternal(). |
|
} |
|
|
|
internal static void EndDisabled() |
|
{ |
|
// Unused, but left here to minimize changes in EditorGUICopy.MultiPropertyFieldInternal(). |
|
} |
|
|
|
internal static float CalcPrefixLabelWidth(GUIContent label, GUIStyle style = null) |
|
{ |
|
if (style == null) |
|
style = EditorStyles.label; |
|
return style.CalcSize(label).x; |
|
} |
|
|
|
internal static void MultiPropertyFieldInternal(Rect position, GUIContent[] subLabels, SerializedProperty valuesIterator, PropertyVisibility visibility, bool[] disabledMask = null, float prefixLabelWidth = -1) |
|
{ |
|
int eCount = subLabels.Length; |
|
float w = (position.width - (eCount - 1) * kSpacingSubLabel) / eCount; |
|
Rect nr = new Rect(position) {width = w}; |
|
float t = EditorGUIUtility.labelWidth; |
|
int l = indentLevel; |
|
indentLevel = 0; |
|
for (int i = 0; i < subLabels.Length; i++) |
|
{ |
|
EditorGUIUtility.labelWidth = prefixLabelWidth > 0 ? prefixLabelWidth : CalcPrefixLabelWidth(subLabels[i]); |
|
|
|
if (disabledMask != null) |
|
BeginDisabled(disabledMask[i]); |
|
EditorGUI.PropertyField(nr, valuesIterator, subLabels[i]); |
|
if (disabledMask != null) |
|
EndDisabled(); |
|
nr.x += w + kSpacingSubLabel; |
|
|
|
switch (visibility) |
|
{ |
|
case PropertyVisibility.All: |
|
valuesIterator.Next(false); |
|
break; |
|
|
|
case PropertyVisibility.OnlyVisible: |
|
valuesIterator.NextVisible(false); |
|
break; |
|
} |
|
} |
|
EditorGUIUtility.labelWidth = t; |
|
indentLevel = l; |
|
} |
|
|
|
internal static bool LabelHasContent(GUIContent label) |
|
{ |
|
if (label == null) |
|
{ |
|
return true; |
|
} |
|
// @TODO: find out why checking for GUIContent.none doesn't work |
|
return label.text != string.Empty || label.image != null; |
|
} |
|
|
|
internal static float indent => indentLevel * kIndentPerLevel; |
|
|
|
internal static Rect MultiFieldPrefixLabel(Rect totalPosition, int id, GUIContent label, int columns) |
|
{ |
|
if (!LabelHasContent(label)) |
|
{ |
|
return EditorGUI.IndentedRect(totalPosition); |
|
} |
|
|
|
if (EditorGUIUtility.wideMode) |
|
{ |
|
Rect labelPosition = new Rect(totalPosition.x + indent, totalPosition.y, EditorGUIUtility.labelWidth - indent, EditorGUIUtility.singleLineHeight); |
|
Rect fieldPosition = totalPosition; |
|
fieldPosition.xMin += EditorGUIUtility.labelWidth + kPrefixPaddingRight; |
|
|
|
// If there are 2 columns we use the same column widths as if there had been 3 columns |
|
// in order to make columns line up neatly. |
|
if (columns == 2) |
|
{ |
|
float columnWidth = (fieldPosition.width - (3 - 1) * kSpacingSubLabel) / 3f; |
|
fieldPosition.xMax -= (columnWidth + kSpacingSubLabel); |
|
} |
|
|
|
EditorGUI.HandlePrefixLabel(totalPosition, labelPosition, label, id); |
|
return fieldPosition; |
|
} |
|
else |
|
{ |
|
Rect labelPosition = new Rect(totalPosition.x + indent, totalPosition.y, totalPosition.width - indent, EditorGUIUtility.singleLineHeight); |
|
Rect fieldPosition = totalPosition; |
|
fieldPosition.xMin += indent + kIndentPerLevel; |
|
fieldPosition.yMin += EditorGUIUtility.singleLineHeight + kVerticalSpacingMultiField; |
|
EditorGUI.HandlePrefixLabel(totalPosition, labelPosition, label, id); |
|
return fieldPosition; |
|
} |
|
} |
|
} |
|
#endif |
|
}
|
|
|