using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace Jovian.InspectorTools { public class UniformVector3Attribute : PropertyAttribute { public UniformVector3Attribute() { } } } #if UNITY_EDITOR namespace Jovian.InspectorTools.Internal { [CustomPropertyDrawer(typeof(UniformVector3Attribute))] public class UniformVector3AttributeDrawer : PropertyDrawer { private GUIContent linkedIconContent; private GUIContent unlinkedIconContent; private GUIStyle linkButtonStyle; private const int CHILD_COUNT = 3; private const float LABEL_WIDTH = 13f; private const float COMBINED_LABEL_WIDTH = 26f; private const float INDENT_WIDTH = 15f; private const float CHILD_HORIZONTAL_SPACING = 4f; private static readonly string[] childPropertyNames = new string[] { "x", "y", "z" }; private static bool IsCompactLayout => Screen.width <= 333f; private static bool IsNoLabelLayout => Screen.width <= 240f; private enum LinkState { Unknown, Linked, Unlinked } private LinkState linkState = LinkState.Unknown; public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { if(IsCompactLayout) { // Unity Vector2 + Vector3 go multi-line at this point return EditorGUIUtility.singleLineHeight * 2f + EditorGUIUtility.standardVerticalSpacing; } return EditorGUIUtility.singleLineHeight; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if(property.propertyType != SerializedPropertyType.Vector3) { GUI.Label(position, "Error, SerializedProperty must be 'Vector3' type"); return; } if(linkedIconContent == null || unlinkedIconContent == null || linkButtonStyle == null) { linkedIconContent = EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Linked" : "Linked"); unlinkedIconContent = EditorGUIUtility.IconContent(EditorGUIUtility.isProSkin ? "d_Unlinked" : "Unlinked"); linkButtonStyle = new GUIStyle(GUI.skin.button); linkButtonStyle.padding = new RectOffset(1, 1, 0, 0); linkButtonStyle.imagePosition = ImagePosition.ImageOnly; } if(linkState == LinkState.Unknown) { linkState = GetLinkState(property); } EditorGUI.BeginProperty(position, label, property); if(IsCompactLayout) { EditorGUIUtility.labelWidth = position.width; } Rect labelRect = new Rect(position) { height = EditorGUIUtility.singleLineHeight }; Rect fieldRect = EditorGUI.PrefixLabel(labelRect, label); fieldRect.height = EditorGUIUtility.singleLineHeight; float height = Mathf.Min(20f, position.height) - 2f; Rect buttonRect = new Rect(fieldRect); buttonRect.width = 18f; buttonRect.height = height; buttonRect.y += 1f; buttonRect.x -= buttonRect.width + 2f; if(GUI.Button(buttonRect, linkState == LinkState.Linked ? linkedIconContent : unlinkedIconContent, linkButtonStyle)) { linkState = linkState == LinkState.Linked ? LinkState.Unlinked : LinkState.Linked; } if(IsCompactLayout) { fieldRect.y = labelRect.yMax + EditorGUIUtility.standardVerticalSpacing; fieldRect.x = labelRect.x + (IsNoLabelLayout ? 0f : INDENT_WIDTH); // 15 = single indent fieldRect.width = position.xMax - fieldRect.xMin; } if(linkState == LinkState.Linked) { EditorGUIUtility.labelWidth = IsNoLabelLayout ? 0.01f : COMBINED_LABEL_WIDTH; SerializedProperty childProperty = property.FindPropertyRelative(childPropertyNames[0]); EditorGUI.BeginChangeCheck(); float newValue = EditorGUI.FloatField(fieldRect, "XYZ", childProperty.floatValue); if(EditorGUI.EndChangeCheck()) { for(int i = 0; i < CHILD_COUNT; i++) { SerializedProperty otherChildProperty = property.FindPropertyRelative(childPropertyNames[i]); otherChildProperty.floatValue = newValue; } } } else { EditorGUIUtility.labelWidth = IsNoLabelLayout ? 0.01f : LABEL_WIDTH; float childSpacing = IsNoLabelLayout ? CHILD_HORIZONTAL_SPACING / 2f : CHILD_HORIZONTAL_SPACING; float childWidth = ((fieldRect.width + childSpacing) / CHILD_COUNT) - childSpacing; for(int i = 0; i < CHILD_COUNT; i++) { SerializedProperty childProperty = property.FindPropertyRelative(childPropertyNames[i]); Rect childRect = new Rect(fieldRect); childRect.width = childWidth; // add spacing childRect.x += i * (childWidth + childSpacing); EditorGUI.PropertyField(childRect, childProperty); } } EditorGUI.EndProperty(); } private LinkState GetLinkState(SerializedProperty property) { float value0 = property.FindPropertyRelative(childPropertyNames[0]).floatValue; for(int i = 1; i < CHILD_COUNT; i++) { SerializedProperty childProperty = property.FindPropertyRelative(childPropertyNames[i]); if(Mathf.Approximately(value0, childProperty.floatValue) == false) { return LinkState.Unlinked; } } return LinkState.Linked; } } } #endif