using System; using System.Reflection; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.Splines; using UnityEngine.UIElements; namespace Unity.Cinemachine.Editor { static class SplineDataInspectorUtility { public delegate ISplineContainer GetSplineDelegate(); public delegate T GetDefaultValueDelegate(); public const string ItemIndexTooltip = "The position on the Spline at which this data point will take effect. " + "The value is interpreted according to the Index Unit setting."; public static VisualElement CreatePathUnitField(SerializedProperty splineDataProp, GetSplineDelegate getSpline) { var indexUnitProp = splineDataProp.FindPropertyRelative("m_IndexUnit"); var enumField = new EnumField(indexUnitProp.displayName, (PathIndexUnit)indexUnitProp.enumValueIndex) { tooltip = "Defines how to interpret the Index field for each data point. " + "Knot is the recommended value because it remains robust if the spline points change." }; enumField.RegisterValueChangedCallback((evt) => { var newIndexUnit = (PathIndexUnit)evt.newValue; var spline = getSpline?.Invoke(); if (spline != null && newIndexUnit != (PathIndexUnit)indexUnitProp.intValue) { Undo.RecordObject(splineDataProp.serializedObject.targetObject, "Change Index Unit"); splineDataProp.serializedObject.Update(); ConvertPathUnit(splineDataProp, spline, 0, newIndexUnit); splineDataProp.serializedObject.ApplyModifiedProperties(); } }); enumField.TrackPropertyValue(indexUnitProp, (p) => enumField.value = (PathIndexUnit)indexUnitProp.enumValueIndex); enumField.TrackAnyUserActivity(() => enumField.SetEnabled(getSpline?.Invoke() != null)); enumField.AddToClassList(InspectorUtility.AlignFieldClassName); return enumField; } static void ConvertPathUnit( SerializedProperty splineDataProp, ISplineContainer container, int splineIndex, PathIndexUnit newIndexUnit) { if (container == null || container.Splines.Count == 0) return; var arrayProp = splineDataProp.FindPropertyRelative("m_DataPoints"); var pathUnitProp = splineDataProp.FindPropertyRelative("m_IndexUnit"); var from = (PathIndexUnit)Enum.GetValues(typeof(PathIndexUnit)).GetValue(pathUnitProp.enumValueIndex); var spline = container.Splines[splineIndex]; var transform = container is Component component ? component.transform.localToWorldMatrix : Matrix4x4.identity; using var native = new NativeSpline(spline, transform); for (int i = 0, c = arrayProp.arraySize; i < c; ++i) { var point = arrayProp.GetArrayElementAtIndex(i); var index = point.FindPropertyRelative("m_Index"); index.floatValue = native.ConvertIndexUnit(index.floatValue, from, newIndexUnit); } pathUnitProp.enumValueIndex = (int)newIndexUnit; } public static ListView CreateDataListField( SplineData splineData, SerializedProperty splineDataProp, GetSplineDelegate getSpline, GetDefaultValueDelegate getDefaultValue = null) { var sortMethod = splineData.GetType().GetMethod("ForceSort", BindingFlags.Instance | BindingFlags.NonPublic); var setDataPointMethod = splineData.GetType().GetMethod("SetDataPoint", BindingFlags.Instance | BindingFlags.Public); var pathUnitProp = splineDataProp.FindPropertyRelative("m_IndexUnit"); var arrayProp = splineDataProp.FindPropertyRelative("m_DataPoints"); var list = new ListView { reorderable = false, showBorder = true, showFoldoutHeader = false, showBoundCollectionSize = false, showAddRemoveFooter = true, virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight, showAlternatingRowBackgrounds = AlternatingRowBackground.None }; list.BindProperty(arrayProp); // When we add the first item, make sure to use the default value var button = list.Q