Maze / Library /PackageCache /com.unity.timeline@1.6.5 /Editor /inspectors /TimelineInspectorUtility.cs
| using System; | |
| using System.Linq; | |
| using UnityEngine; | |
| using UnityEngine.Timeline; | |
| namespace UnityEditor.Timeline | |
| { | |
| [] | |
| enum InputEvent | |
| { | |
| None = 0, | |
| DragEnter = 1, | |
| DragExit = 2, | |
| Drag = 4, | |
| KeyboardInput = 8 | |
| } | |
| static class InputEventMethods | |
| { | |
| public static bool InputHasBegun(this InputEvent evt) | |
| { | |
| return evt == InputEvent.DragEnter || evt == InputEvent.KeyboardInput; | |
| } | |
| } | |
| static class TimelineInspectorUtility | |
| { | |
| internal static class Styles | |
| { | |
| public static readonly GUIContent SecondsPrefix = L10n.TextContent("s", "Seconds"); | |
| public static readonly GUIContent FramesPrefix = L10n.TextContent("f", "Frames"); | |
| } | |
| public static void TimeField(SerializedProperty property, GUIContent label, bool readOnly, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent) | |
| { | |
| var rect = EditorGUILayout.GetControlRect(); | |
| TimeField(rect, property, label, readOnly, frameRate, minValue, maxValue, ref inputEvent); | |
| } | |
| // Display Time related properties in frames and seconds | |
| public static void TimeField(Rect rect, SerializedProperty property, GUIContent label, bool readOnly, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent) | |
| { | |
| using (var propertyScope = new PropertyScope(rect, label, property)) | |
| { | |
| GUIContent title = propertyScope.content; | |
| rect = EditorGUI.PrefixLabel(rect, title); | |
| using (new IndentLevelScope(0)) | |
| using (new LabelWidthScope((int)EditorGUI.kMiniLabelW)) | |
| using (new GUIMixedValueScope(property.hasMultipleDifferentValues)) | |
| { | |
| var secondsRect = new Rect(rect.xMin, rect.yMin, rect.width / 2 - EditorGUI.kSpacingSubLabel, rect.height); | |
| var framesRect = new Rect(rect.xMin + rect.width / 2, rect.yMin, rect.width / 2, rect.height); | |
| if (readOnly) | |
| { | |
| EditorGUI.FloatField(secondsRect, Styles.SecondsPrefix, (float)property.doubleValue, EditorStyles.label); | |
| } | |
| else | |
| { | |
| EditorGUI.BeginChangeCheck(); | |
| DelayedAndDraggableDoubleField(secondsRect, Styles.SecondsPrefix, property, ref inputEvent); | |
| if (EditorGUI.EndChangeCheck()) | |
| { | |
| property.doubleValue = Clamp(property.doubleValue, minValue, maxValue); | |
| } | |
| } | |
| if (frameRate > TimeUtility.kTimeEpsilon) | |
| { | |
| EditorGUI.BeginChangeCheck(); | |
| double time = property.doubleValue; | |
| int frames = TimeUtility.ToFrames(time, frameRate); | |
| double exactFrames = TimeUtility.ToExactFrames(time, frameRate); | |
| bool useIntField = TimeUtility.OnFrameBoundary(time, frameRate); | |
| if (readOnly) | |
| { | |
| if (useIntField) | |
| EditorGUI.IntField(framesRect, Styles.FramesPrefix, frames, EditorStyles.label); | |
| else | |
| EditorGUI.DoubleField(framesRect, Styles.FramesPrefix, exactFrames, EditorStyles.label); | |
| } | |
| else | |
| { | |
| if (useIntField) | |
| { | |
| int newFrames = DelayedAndDraggableIntField(framesRect, Styles.FramesPrefix, frames, ref inputEvent); | |
| time = Math.Max(0, TimeUtility.FromFrames(newFrames, frameRate)); | |
| } | |
| else | |
| { | |
| double newExactFrames = DelayedAndDraggableDoubleField(framesRect, Styles.FramesPrefix, exactFrames, ref inputEvent); | |
| time = Math.Max(0, TimeUtility.FromFrames((int)Math.Floor(newExactFrames), frameRate)); | |
| } | |
| } | |
| if (EditorGUI.EndChangeCheck()) | |
| { | |
| property.doubleValue = Clamp(time, minValue, maxValue); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| public static double TimeFieldUsingTimeReference( | |
| GUIContent label, double time, bool readOnly, bool showMixed, double frameRate, double minValue, | |
| double maxValue, ref InputEvent inputEvent) | |
| { | |
| var state = TimelineWindow.instance.state; | |
| var needsTimeConversion = state != null && state.timeReferenceMode == TimeReferenceMode.Global; | |
| if (needsTimeConversion) | |
| time = state.editSequence.ToGlobalTime(time); | |
| var t = TimeField(label, time, readOnly, showMixed, frameRate, minValue, maxValue, ref inputEvent); | |
| if (needsTimeConversion) | |
| t = state.editSequence.ToLocalTime(t); | |
| return t; | |
| } | |
| public static double DurationFieldUsingTimeReference( | |
| GUIContent label, double start, double end, bool readOnly, bool showMixed, double frameRate, | |
| double minValue, double maxValue, ref InputEvent inputEvent) | |
| { | |
| var state = TimelineWindow.instance.state; | |
| var needsTimeConversion = state != null && state.timeReferenceMode == TimeReferenceMode.Global; | |
| if (needsTimeConversion) | |
| { | |
| start = state.editSequence.ToGlobalTime(start); | |
| end = state.editSequence.ToGlobalTime(end); | |
| } | |
| var duration = end - start; | |
| var t = TimeField(label, duration, readOnly, showMixed, frameRate, minValue, maxValue, ref inputEvent); | |
| end = start + t; | |
| if (needsTimeConversion) | |
| { | |
| start = state.editSequence.ToLocalTime(start); | |
| end = state.editSequence.ToLocalTime(end); | |
| } | |
| return end - start; | |
| } | |
| public static double TimeField(Rect rect, GUIContent label, double time, bool readOnly, bool showMixed, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent) | |
| { | |
| using (new HorizontalScope(label, GUIStyle.none)) | |
| { | |
| rect = EditorGUI.PrefixLabel(rect, label); | |
| using (new IndentLevelScope(0)) | |
| using (new LabelWidthScope((int)EditorGUI.kMiniLabelW)) | |
| using (new GUIMixedValueScope(showMixed)) | |
| { | |
| var secondsRect = new Rect(rect.xMin, rect.yMin, rect.width / 2 - EditorGUI.kSpacingSubLabel, rect.height); | |
| var framesRect = new Rect(rect.xMin + rect.width / 2, rect.yMin, rect.width / 2, rect.height); | |
| if (readOnly) | |
| { | |
| EditorGUI.FloatField(secondsRect, Styles.SecondsPrefix, (float)time, EditorStyles.label); | |
| } | |
| else | |
| { | |
| time = DelayedAndDraggableDoubleField(secondsRect, Styles.SecondsPrefix, time, ref inputEvent); | |
| } | |
| if (frameRate > TimeUtility.kTimeEpsilon) | |
| { | |
| int frames = TimeUtility.ToFrames(time, frameRate); | |
| double exactFrames = TimeUtility.ToExactFrames(time, frameRate); | |
| bool useIntField = TimeUtility.OnFrameBoundary(time, frameRate); | |
| if (readOnly) | |
| { | |
| if (useIntField) | |
| EditorGUI.IntField(framesRect, Styles.FramesPrefix, frames, EditorStyles.label); | |
| else | |
| EditorGUI.FloatField(framesRect, Styles.FramesPrefix, (float)exactFrames, EditorStyles.label); | |
| } | |
| else | |
| { | |
| double newTime; | |
| EditorGUI.BeginChangeCheck(); | |
| if (useIntField) | |
| { | |
| int newFrames = DelayedAndDraggableIntField(framesRect, Styles.FramesPrefix, frames, ref inputEvent); | |
| newTime = Math.Max(0, TimeUtility.FromFrames(newFrames, frameRate)); | |
| } | |
| else | |
| { | |
| double newExactFrames = DelayedAndDraggableDoubleField(framesRect, Styles.FramesPrefix, exactFrames, ref inputEvent); | |
| newTime = Math.Max(0, TimeUtility.FromFrames((int)Math.Floor(newExactFrames), frameRate)); | |
| } | |
| if (EditorGUI.EndChangeCheck()) | |
| { | |
| time = newTime; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return Clamp(time, minValue, maxValue); | |
| } | |
| public static double TimeField(GUIContent label, double time, bool readOnly, bool showMixed, double frameRate, double minValue, double maxValue, ref InputEvent inputEvent) | |
| { | |
| var rect = EditorGUILayout.GetControlRect(); | |
| return TimeField(rect, label, time, readOnly, showMixed, frameRate, minValue, maxValue, ref inputEvent); | |
| } | |
| static InputEvent InputEventType(Rect rect, int id) | |
| { | |
| var evt = Event.current; | |
| switch (evt.GetTypeForControl(id)) | |
| { | |
| case EventType.MouseDown: | |
| if (rect.Contains(evt.mousePosition) && evt.button == 0) | |
| { | |
| return InputEvent.DragEnter; | |
| } | |
| break; | |
| case EventType.MouseUp: | |
| if (GUIUtility.hotControl == id) | |
| { | |
| return InputEvent.DragExit; | |
| } | |
| break; | |
| case EventType.MouseDrag: | |
| if (GUIUtility.hotControl == id) | |
| { | |
| return InputEvent.Drag; | |
| } | |
| break; | |
| case EventType.KeyDown: | |
| if (GUIUtility.hotControl == id && evt.keyCode == KeyCode.Escape) | |
| { | |
| return InputEvent.DragExit; | |
| } | |
| break; | |
| } | |
| return InputEvent.None; | |
| } | |
| static double DelayedAndDraggableDoubleField(Rect rect, GUIContent label, double value, ref InputEvent inputEvent, double dragSensitivity) | |
| { | |
| var id = GUIUtility.GetControlID(FocusType.Keyboard); | |
| var fieldRect = EditorGUI.PrefixLabel(rect, id, label); | |
| rect.xMax = fieldRect.x; | |
| double refValue = value; | |
| long dummy = 0; | |
| inputEvent |= InputEventType(rect, id); | |
| EditorGUI.DragNumberValue(rect, id, true, ref refValue, ref dummy, dragSensitivity); | |
| EditorGUI.BeginChangeCheck(); | |
| var result = EditorGUI.DelayedDoubleFieldInternal(fieldRect, GUIContent.none, refValue, EditorStyles.numberField); | |
| if (EditorGUI.EndChangeCheck()) | |
| inputEvent |= InputEvent.KeyboardInput; | |
| return result; | |
| } | |
| static int DelayedAndDraggableIntField(Rect rect, GUIContent label, int value, ref InputEvent inputEvent, long dragSensitivity) | |
| { | |
| var id = GUIUtility.GetControlID(FocusType.Keyboard); | |
| var fieldRect = EditorGUI.PrefixLabel(rect, id, label); | |
| rect.xMax = fieldRect.x; | |
| double dummy = 0.0; | |
| long refValue = value; | |
| inputEvent |= InputEventType(rect, id); | |
| EditorGUI.DragNumberValue(rect, id, false, ref dummy, ref refValue, dragSensitivity); | |
| EditorGUI.BeginChangeCheck(); | |
| var result = EditorGUI.DelayedIntFieldInternal(fieldRect, GUIContent.none, (int)refValue, EditorStyles.numberField); | |
| if (EditorGUI.EndChangeCheck()) | |
| inputEvent |= InputEvent.KeyboardInput; | |
| return result; | |
| } | |
| internal static double DelayedAndDraggableDoubleField(GUIContent label, double value, ref InputEvent action, double dragSensitivity) | |
| { | |
| var r = EditorGUILayout.s_LastRect = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight); | |
| return DelayedAndDraggableDoubleField(r, label, value, ref action, dragSensitivity); | |
| } | |
| static void DelayedAndDraggableDoubleField(Rect rect, GUIContent label, SerializedProperty property, ref InputEvent inputEvent) | |
| { | |
| EditorGUI.BeginChangeCheck(); | |
| var newValue = DelayedAndDraggableDoubleField(rect, label, property.doubleValue, ref inputEvent); | |
| if (EditorGUI.EndChangeCheck()) | |
| property.doubleValue = newValue; | |
| } | |
| static double DelayedAndDraggableDoubleField(Rect rect, GUIContent label, double value, ref InputEvent inputEvent) | |
| { | |
| var dragSensitivity = NumericFieldDraggerUtility.CalculateFloatDragSensitivity(value); | |
| return DelayedAndDraggableDoubleField(rect, label, value, ref inputEvent, dragSensitivity); | |
| } | |
| static int DelayedAndDraggableIntField(Rect rect, GUIContent label, int value, ref InputEvent inputEvent) | |
| { | |
| var dragSensitivity = NumericFieldDraggerUtility.CalculateIntDragSensitivity(value); | |
| return DelayedAndDraggableIntField(rect, label, value, ref inputEvent, dragSensitivity); | |
| } | |
| internal static T Clamp<T>(this T val, T min, T max) where T : IComparable<T> | |
| { | |
| if (val.CompareTo(min) < 0) return min; | |
| if (val.CompareTo(max) > 0) return max; | |
| return val; | |
| } | |
| public static Editor GetInspectorForObjects(UnityEngine.Object[] objects, Editor previousEditor) | |
| { | |
| // create cached editor throws on assembly reload... | |
| try | |
| { | |
| if (objects.Any(x => x != null)) | |
| { | |
| var director = TimelineWindow.instance.state.editSequence.director; | |
| Editor.CreateCachedEditorWithContext(objects, director, null, ref previousEditor); | |
| return previousEditor; | |
| } | |
| } | |
| catch (Exception) | |
| { } | |
| return null; | |
| } | |
| } | |
| } | |