RoboticArms/Library/PackageCache/com.unity.animation.rigging@68167b505d2b/Editor/Utils/EditorHelpers.cs
2025-11-17 15:16:36 +07:00

222 lines
10 KiB
C#

using System;
using UnityEngine;
using UnityEngine.Animations.Rigging;
using UnityEditorInternal;
namespace UnityEditor.Animations.Rigging
{
/// <summary>
/// Helper class to manage ReorderableList appearance in the editor.
/// </summary>
public static class ReorderableListHelper
{
const int k_NoHeaderHeight = 2;
const int k_ElementHeightPadding = 2;
/// <summary>
/// Creates a ReorderableList using a SerializedProperty array as source.
/// </summary>
/// <param name="obj">SerializedObject owning the SerializedProperty.</param>
/// <param name="property">SerializedProperty array.</param>
/// <param name="draggable">Toggles whether an object is draggable in the list. True when an object is draggable, false otherwise.</param>
/// <param name="displayHeader">Displays the ReorderableList header.</param>
/// <returns>Returns a new ReorderableList.</returns>
public static ReorderableList Create(SerializedObject obj, SerializedProperty property, bool draggable = true, bool displayHeader = false)
{
var list = new ReorderableList(obj, property, draggable, displayHeader, true, true);
list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = list.serializedProperty.GetArrayElementAtIndex(index);
var offset = k_ElementHeightPadding * 0.5f;
rect.y += offset;
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(rect, element, GUIContent.none);
};
list.elementHeight = EditorGUIUtility.singleLineHeight + k_ElementHeightPadding;
if (!displayHeader)
list.headerHeight = k_NoHeaderHeight;
return list;
}
}
/// <summary>
/// Helper class to manage WeightedTransform and WeightedTransformArray appearance in the editor.
/// </summary>
[Obsolete("This is now handled automatically in the inspector for WeightedTransformArray.", false)]
public static class WeightedTransformHelper
{
const int k_NoHeaderHeight = 2;
const int k_ElementHeightPadding = 2;
/// <summary>
/// Creates a ReorderableList using a WeightedTransformArray as source.
/// </summary>
/// <param name="property">The serialized property of the WeightedTransformArray.</param>
/// <param name="array">The source WeightedTransformArray.</param>
/// <param name="range">Range attribute given for weights in WeightedTransform. No boundaries are set if null.</param>
/// <param name="draggable">Toggles whether a WeightedTransform is draggable in the list. True when WeightedTransform is draggable, false otherwise.</param>
/// <param name="displayHeader">Displays the ReorderableList header.</param>
/// <returns>Returns a new ReorderableList for a WeightedTransformArray.</returns>
public static ReorderableList CreateReorderableList(SerializedProperty property, ref WeightedTransformArray array, RangeAttribute range = null, bool draggable = true, bool displayHeader = false)
{
var reorderableList = new ReorderableList(array, typeof(WeightedTransform), draggable, displayHeader, true, true);
reorderableList.drawElementBackgroundCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
// Didn't find a way to register a callback before we repaint the reorderable list to toggle draggable flag.
// Ideally, we'd need a callback canReorderElementCallback that would enable/disable draggable handle.
reorderableList.draggable = !AnimationMode.InAnimationMode() && !Application.isPlaying;
ReorderableList.defaultBehaviours.DrawElementBackground(rect, index, isActive, isFocused, reorderableList.draggable);
};
reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = property.FindPropertyRelative("m_Item" + index);
var offset = k_ElementHeightPadding * 0.5f;
rect.y += offset;
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.BeginChangeCheck();
WeightedTransformOnGUI(rect, element, range);
if (EditorGUI.EndChangeCheck())
{
var transformProperty = element.FindPropertyRelative("transform");
var weightProperty = element.FindPropertyRelative("weight");
reorderableList.list[index] = new WeightedTransform(transformProperty.objectReferenceValue as Transform, weightProperty.floatValue);;
}
};
reorderableList.onCanAddCallback = (ReorderableList list) =>
{
return list.list.Count < WeightedTransformArray.k_MaxLength && !AnimationMode.InAnimationMode() && !Application.isPlaying;
};
reorderableList.onCanRemoveCallback = (ReorderableList list) =>
{
return !AnimationMode.InAnimationMode() && !Application.isPlaying;
};
reorderableList.onAddCallback = (ReorderableList list) =>
{
list.list.Add(WeightedTransform.Default(1f));
};
reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + k_ElementHeightPadding;
if (!displayHeader)
reorderableList.headerHeight = k_NoHeaderHeight;
return reorderableList;
}
/// <summary>
/// Display a single WeightedTransform in the editor.
/// </summary>
/// <param name="rect">Rectangle on the screen to use for the WeightedTransform.</param>
/// <param name="property">Serialized property </param>
/// <param name="range">Range attribute given for weights in WeightedTransform. No boundaries are set if null.</param>
public static void WeightedTransformOnGUI(Rect rect, SerializedProperty property, RangeAttribute range = null) =>
WeightedTransformDrawer.DoGUI(rect, property, range?.min ?? float.NaN, range?.max ?? float.NaN);
}
internal static class MaintainOffsetHelper
{
static readonly string[] k_MaintainOffsetTypeLables = { "None", "Position and Rotation", "Position", "Rotation"};
static readonly int[] k_BitsToIndex = new int[] {0, 2, 3, 1};
static readonly int[] k_IndexToBits = new int[] {0, 3, 1, 2};
public static void DoDropdown(GUIContent label, SerializedProperty maintainPosition, SerializedProperty maintainRotation)
{
int currIndex = k_BitsToIndex[System.Convert.ToInt32(maintainPosition.boolValue) | (System.Convert.ToInt32(maintainRotation.boolValue) << 1)];
int newIndex = EditorGUILayout.Popup(label, currIndex, k_MaintainOffsetTypeLables);
if (newIndex == currIndex)
return;
var bits = k_IndexToBits[newIndex];
maintainPosition.boolValue = (bits & 0x1) != 0;
maintainRotation.boolValue = (bits & 0x2) != 0;
}
}
internal static class EditorHelper
{
private const string EditorFolder = "Packages/com.unity.animation.rigging/Editor/";
private const string ShadersFolder = EditorFolder + "Shaders/";
private const string ShapesFolder = EditorFolder + "Shapes/";
public static Shader LoadShader(string filename)
{
return AssetDatabase.LoadAssetAtPath<Shader>(ShadersFolder + filename);
}
public static Mesh LoadShape(string filename)
{
return AssetDatabase.LoadAssetAtPath<Mesh>(ShapesFolder + filename);
}
public static T GetClosestComponent<T>(Transform transform, Transform root = null)
{
if (transform == null)
return default(T);
var top = (root != null) ? root : transform.root;
while (true)
{
bool gotComponent = transform.TryGetComponent<T>(out T component);
if (gotComponent) return component;
if (transform == top) break;
transform = transform.parent;
}
return default(T);
}
public static void HandleClickSelection(GameObject gameObject, Event evt)
{
if (evt.shift || EditorGUI.actionKey)
{
UnityEngine.Object[] existingSelection = Selection.objects;
// For shift, we check if EXACTLY the active GO is hovered by mouse and then subtract. Otherwise additive.
// For control/cmd, we check if ANY of the selected GO is hovered by mouse and then subtract. Otherwise additive.
// Control/cmd takes priority over shift.
bool subtractFromSelection = EditorGUI.actionKey ? Selection.Contains(gameObject) : Selection.activeGameObject == gameObject;
if (subtractFromSelection)
{
// subtract from selection
var newSelection = new UnityEngine.Object[existingSelection.Length - 1];
int index = Array.IndexOf(existingSelection, gameObject);
System.Array.Copy(existingSelection, newSelection, index);
System.Array.Copy(existingSelection, index + 1, newSelection, index, newSelection.Length - index);
Selection.objects = newSelection;
}
else
{
// add to selection
var newSelection = new UnityEngine.Object[existingSelection.Length + 1];
System.Array.Copy(existingSelection, newSelection, existingSelection.Length);
newSelection[existingSelection.Length] = gameObject;
Selection.objects = newSelection;
}
}
else
Selection.activeObject = gameObject;
}
}
}