/************************************************************************************************************************/ /// <summary>Draws the GUI the the target transition's main property.</summary> protected virtual void DoMainPropertyGUI(Rect area, out Rect labelArea, SerializedProperty rootProperty, SerializedProperty mainProperty) { labelArea = area; if (mainProperty == null) { return; } var fullArea = area; labelArea = AnimancerGUI.StealFromLeft(ref area, EditorGUIUtility.labelWidth, AnimancerGUI.StandardSpacing); var mainPropertyReferenceIsMissing = mainProperty.propertyType == SerializedPropertyType.ObjectReference && mainProperty.objectReferenceValue == null; var hierarchyMode = EditorGUIUtility.hierarchyMode; EditorGUIUtility.hierarchyMode = true; if (rootProperty.propertyType == SerializedPropertyType.ManagedReference) { if (rootProperty.isExpanded || _Mode == Mode.AlwaysExpanded) { EditorGUI.indentLevel++; AnimancerGUI.NextVerticalArea(ref fullArea); using (ObjectPool.Disposable.AcquireContent(out var label, mainProperty)) EditorGUI.PropertyField(fullArea, mainProperty, label, true); EditorGUI.indentLevel--; } } else { var indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; EditorGUI.PropertyField(area, mainProperty, GUIContent.none, true); EditorGUI.indentLevel = indentLevel; } EditorGUIUtility.hierarchyMode = hierarchyMode; // If the main Object reference was just assigned and all fields were at their type default, // reset the value to run its default constructor and field initializers then reassign the reference. var reference = mainProperty.objectReferenceValue; if (mainPropertyReferenceIsMissing && reference != null) { mainProperty.objectReferenceValue = null; if (Serialization.IsDefaultValueByType(rootProperty)) { rootProperty.GetAccessor().ResetValue(rootProperty); } mainProperty.objectReferenceValue = reference; } }
/************************************************************************************************************************/ /// <summary>Draws the root property of a transition with an optional main property on the same line.</summary> public void DoHeaderGUI(ref Rect area, SerializedProperty rootProperty, SerializedProperty mainProperty, GUIContent label, bool isPreviewing) { area.height = AnimancerGUI.LineHeight; var labelArea = area; AnimancerGUI.NextVerticalArea(ref area); if (rootProperty.propertyType != SerializedPropertyType.ManagedReference) { DoPreviewButtonGUI(ref labelArea, rootProperty, isPreviewing); } // Draw the Root Property after the Main Property to give better spacing between the label and field. // Drawing the main property might assign its details to the label so we keep our own copy. using (ObjectPool.Disposable.AcquireContent(out var rootLabel, label.text, label.tooltip)) { // Main Property. DoMainPropertyGUI(labelArea, out labelArea, rootProperty, mainProperty); // Root Property. rootLabel = EditorGUI.BeginProperty(labelArea, rootLabel, rootProperty); EditorGUI.LabelField(labelArea, rootLabel); EditorGUI.EndProperty(); if (_Mode != Mode.AlwaysExpanded) { rootProperty.isExpanded = EditorGUI.Foldout(labelArea, rootProperty.isExpanded, GUIContent.none, true); } } }
/************************************************************************************************************************/ private void DoChildPropertiesGUI(Rect area, float headerHeight, SerializedProperty property) { if (!property.isExpanded && _Mode != Mode.AlwaysExpanded) { return; } area.y += headerHeight + AnimancerGUI.StandardSpacing; EditorGUI.indentLevel++; var rootProperty = property; property = property.Copy(); SerializedProperty eventsProperty = null; var depth = property.depth; property.NextVisible(true); while (property.depth > depth) { // Grab the Events property and draw it last. if (eventsProperty == null && property.propertyPath.EndsWith("._Events")) { eventsProperty = property.Copy(); } else if (MainPropertyPathSuffix == null || !property.propertyPath.EndsWith(MainPropertyPathSuffix)) { if (eventsProperty != null) { var accessor = property.GetAccessor(); if (accessor.Field.IsDefined(typeof(DrawAfterEventsAttribute), false)) { var eventsLabel = AnimancerGUI.TempContent(eventsProperty); DoPropertyGUI(ref area, rootProperty, eventsProperty, eventsLabel); AnimancerGUI.NextVerticalArea(ref area); eventsProperty = null; } } var label = AnimancerGUI.TempContent(property); DoPropertyGUI(ref area, rootProperty, property, label); AnimancerGUI.NextVerticalArea(ref area); } if (!property.NextVisible(false)) { break; } } if (eventsProperty != null) { var label = AnimancerGUI.TempContent(eventsProperty); DoPropertyGUI(ref area, rootProperty, eventsProperty, label); } EditorGUI.indentLevel--; }
/// <summary> /// If the `property` is a "Start Time" field, this method draws it as well as the "End Time" below it and /// returns true. /// </summary> public static bool TryDoStartTimeField(ref Rect area, SerializedProperty rootProperty, SerializedProperty property, GUIContent label) { if (!property.propertyPath.EndsWith("." + NormalizedStartTimeFieldName)) { return(false); } // Start Time. label.text = AnimancerGUI.GetNarrowText("Start Time"); AnimationTimeAttribute.nextDefaultValue = AnimancerEvent.Sequence.GetDefaultNormalizedStartTime(Context.Transition.Speed); EditorGUI.PropertyField(area, property, label, false); AnimancerGUI.NextVerticalArea(ref area); // End Time. var events = rootProperty.FindPropertyRelative("_Events"); using (var context = SerializableEventSequenceDrawer.Context.Get(events)) { var areaCopy = area; var index = Mathf.Max(0, context.Times.Count - 1); SerializableEventSequenceDrawer.DoTimeGUI(ref areaCopy, context, index, true); } return(true); }
/// <summary> /// If the `property` is a "Start Time" field, this method draws it as well as the "End Time" below it and /// returns true. /// </summary> public static bool TryDoStartTimeField(ref Rect area, SerializedProperty rootProperty, SerializedProperty property, GUIContent label) { if (!property.propertyPath.EndsWith("." + NormalizedStartTimeFieldName)) { return(false); } // Start Time. label.text = AnimancerGUI.GetNarrowText("Start Time"); var length = Context.MaximumDuration; var defaultStartTime = AnimancerEvent.Sequence.GetDefaultNormalizedStartTime(Context.Transition.Speed); AnimancerGUI.DoOptionalTimeField(ref area, label, property, true, length, defaultStartTime); AnimancerGUI.NextVerticalArea(ref area); // End Time. var events = rootProperty.FindPropertyRelative("_Events"); using (var context = EventSequenceDrawer.Context.Get(events)) { var areaCopy = area; var index = Mathf.Max(0, context.TimeCount - 1); string callbackLabel; EventSequenceDrawer.DoEventTimeGUI(ref areaCopy, context, index, true, out callbackLabel); } return(true); }
/************************************************************************************************************************/ private void DoHeaderGUI(ref Rect area, GUIContent label, Context context) { area.height = AnimancerGUI.LineHeight; var headerArea = area; AnimancerGUI.NextVerticalArea(ref area); label = EditorGUI.BeginProperty(headerArea, label, context.Property); if (!context.Property.hasMultipleDifferentValues) { var addEventArea = AnimancerGUI.StealFromRight(ref headerArea, headerArea.height, AnimancerGUI.StandardSpacing); DoAddRemoveEventButtonGUI(addEventArea, context); } if (context.TransitionContext != null && context.TransitionContext.Transition != null) { EditorGUI.EndProperty(); TimelineGUI.DoGUI(headerArea, context, out var addEventNormalizedTime); if (!float.IsNaN(addEventNormalizedTime)) { AddEvent(context, addEventNormalizedTime); } } else { label.text = AnimancerGUI.GetNarrowText(label.text); string summary; if (context.Times.Count == 0) { summary = "[0] End Time 1"; } else { var index = context.Times.Count - 1; var endTime = context.Times.GetElement(index).floatValue; summary = $"[{index}] End Time {endTime:G3}"; } using (ObjectPool.Disposable.AcquireContent(out var content, summary)) EditorGUI.LabelField(headerArea, label, content); EditorGUI.EndProperty(); } EditorGUI.BeginChangeCheck(); context.Property.isExpanded = EditorGUI.Foldout(headerArea, context.Property.isExpanded, GUIContent.none, true); if (EditorGUI.EndChangeCheck()) { context.SelectedEvent = -1; } }
private static void DrawSpriteFrames(AnimationClip clip) { var keyframes = GetSpriteReferences(clip); if (keyframes == null) { return; } for (int i = 0; i < keyframes.Length; i++) { var keyframe = keyframes[i]; var sprite = keyframe.value as Sprite; if (sprite != null) { if (_FrameCache == null) { _FrameCache = new ConversionCache <int, string>( (value) => $"Frame: {value}"); _TimeCache = new ConversionCache <float, string>( (value) => $"Time: {value}s"); } var texture = sprite.texture; var area = GUILayoutUtility.GetRect(0, AnimancerGUI.LineHeight * 4); var width = area.width; var rect = sprite.rect; area.width = area.height * rect.width / rect.height; rect.x /= texture.width; rect.y /= texture.height; rect.width /= texture.width; rect.height /= texture.height; GUI.DrawTextureWithTexCoords(area, texture, rect); var offset = area.width + AnimancerGUI.StandardSpacing; area.x += offset; area.width = width - offset; area.height = AnimancerGUI.LineHeight; area.y += Mathf.Round(area.height * 0.5f); GUI.Label(area, _FrameCache.Convert(i)); AnimancerGUI.NextVerticalArea(ref area); GUI.Label(area, _TimeCache.Convert(keyframe.time)); AnimancerGUI.NextVerticalArea(ref area); GUI.Label(area, sprite.name); } } }
private void DoHeaderGUI(ref Rect area, GUIContent label, Context context) { area.height = AnimancerGUI.LineHeight; var headerArea = area; AnimancerGUI.NextVerticalArea(ref area); label = EditorGUI.BeginProperty(headerArea, label, context.Property); var addEventArea = AnimancerGUI.StealFromRight(ref headerArea, headerArea.height, AnimancerGUI.StandardSpacing); DoAddEventButtonGUI(addEventArea, context); if (context.TransitionContext != null && context.TransitionContext.Transition != null) { EditorGUI.EndProperty(); float addEventNormalizedTime; TimeRuler.DoGUI(headerArea, context, out addEventNormalizedTime); if (!float.IsNaN(addEventNormalizedTime)) { AddEvent(context, addEventNormalizedTime); } } else { label.text = AnimancerGUI.GetNarrowText(label.text); var summary = AnimancerGUI.TempContent(); if (context.TimeCount == 0) { summary.text = "[0] End Time 1"; } else { var index = context.TimeCount - 1;; var endTime = context.GetTime(index).floatValue; summary.text = string.Concat("[", index.ToString(), "] End Time ", endTime.ToString("G3")); } EditorGUI.LabelField(headerArea, label, summary); EditorGUI.EndProperty(); } EditorGUI.BeginChangeCheck(); context.Property.isExpanded = EditorGUI.Foldout(headerArea, context.Property.isExpanded, GUIContent.none, true); if (EditorGUI.EndChangeCheck()) { context.SelectedEvent = -1; } }
/// <summary>Draws the GUI for the `animancerEvent`.</summary> public static void Draw(ref Rect area, string name, AnimancerEvent animancerEvent) { area.height = AnimancerGUI.LineHeight; if (_EventTimeCache == null) { _EventTimeCache = new ConversionCache <float, string>((time) => float.IsNaN(time) ? "Time = Auto" : $"Time = {time.ToStringCached()}x"); } EditorGUI.LabelField(area, name, _EventTimeCache.Convert(animancerEvent.normalizedTime)); AnimancerGUI.NextVerticalArea(ref area); EditorGUI.indentLevel++; DrawInvocationList(ref area, animancerEvent.callback); EditorGUI.indentLevel--; }
/************************************************************************************************************************/ /// <summary>Draws a group of <see cref="NormalizedPixelField"/>s.</summary> public static void DoGroupGUI(SerializedProperty baseProperty, GUIContent label, NormalizedPixelField[] fields) { var height = (AnimancerGUI.LineHeight + AnimancerGUI.StandardSpacing) * (fields.Length + 1); var area = GUILayoutUtility.GetRect(0, height); area.height = AnimancerGUI.LineHeight; label = EditorGUI.BeginProperty(area, label, baseProperty); GUI.Label(area, label); EditorGUI.EndProperty(); EditorGUI.indentLevel++; for (int i = 0; i < fields.Length; i++) { AnimancerGUI.NextVerticalArea(ref area); fields[i].DoTwinFloatFieldGUI(area); } EditorGUI.indentLevel--; }
/************************************************************************************************************************/ /// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary> public static void Draw(ref Rect area, Delegate del) { area.height = AnimancerGUI.LineHeight; if (del == null) { EditorGUI.LabelField(area, "Callback", "Null"); AnimancerGUI.NextVerticalArea(ref area); return; } var method = del.Method; EditorGUI.LabelField(area, "Method", method.Name); AnimancerGUI.NextVerticalArea(ref area); var target = del.Target; if (target is Object obj) { var enabled = GUI.enabled; GUI.enabled = false; EditorGUI.ObjectField(area, "Target", obj, obj.GetType(), true); GUI.enabled = enabled; } else if (target != null) { EditorGUI.LabelField(area, "Target", target.ToString()); } else { EditorGUI.LabelField(area, "Declaring Type", method.DeclaringType.GetNameCS()); } AnimancerGUI.NextVerticalArea(ref area); }
/************************************************************************************************************************/ /// <summary>Draws the target and name of the specified <see cref="Delegate"/>.</summary> public static void DrawInvocationList(ref Rect area, MulticastDelegate del) { if (del == null) { EditorGUI.LabelField(area, "Delegate", "Null"); AnimancerGUI.NextVerticalArea(ref area); return; } var delegates = GetInvocationListIfMulticast(del); if (delegates == null) { Draw(ref area, del); } else { for (int i = 0; i < delegates.Length; i++) { Draw(ref area, delegates[i]); } } }
/// <summary>Draws the time field for the event at the specified `index`.</summary> public static void DoTimeGUI(ref Rect area, Context context, int index, bool autoSort, string timeLabel, float defaultTime, bool isEndEvent) { EditorGUI.BeginChangeCheck(); area.height = EventTimeAttribute.GetPropertyHeight(null, null); var timeArea = area; AnimancerGUI.NextVerticalArea(ref area); float normalizedTime; using (ObjectPool.Disposable.AcquireContent(out var label, timeLabel, isEndEvent ? Strings.Tooltips.EndTime : Strings.Tooltips.CallbackTime)) { var length = context.TransitionContext?.MaximumDuration ?? float.NaN; if (index < context.Times.Count) { var timeProperty = context.Times.GetElement(index); if (timeProperty == null)// Multi-selection screwed up the property retrieval. { EditorGUI.BeginChangeCheck(); label = EditorGUI.BeginProperty(timeArea, label, context.Times.Property); if (isEndEvent) { AnimationTimeAttribute.nextDefaultValue = defaultTime; } normalizedTime = float.NaN; EventTimeAttribute.OnGUI(timeArea, label, ref normalizedTime); EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck()) { context.Times.Count = context.Times.Count; timeProperty = context.Times.GetElement(index); timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, index, normalizedTime); } } else// Event time property was correctly retrieved. { var wasEditingTextField = EditorGUIUtility.editingTextField; if (!wasEditingTextField) { _PreviousTime = float.NaN; } EditorGUI.BeginChangeCheck(); label = EditorGUI.BeginProperty(timeArea, label, timeProperty); if (isEndEvent) { AnimationTimeAttribute.nextDefaultValue = defaultTime; } normalizedTime = timeProperty.floatValue; EventTimeAttribute.OnGUI(timeArea, label, ref normalizedTime); EditorGUI.EndProperty(); if (AnimancerGUI.TryUseClickEvent(timeArea, 2)) { normalizedTime = float.NaN; } var isEditingTextField = EditorGUIUtility.editingTextField; if (EditorGUI.EndChangeCheck() || (wasEditingTextField && !isEditingTextField)) { if (float.IsNaN(normalizedTime)) { RemoveEvent(context, index); AnimancerGUI.Deselect(); } else if (isEndEvent) { timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, index, normalizedTime); } else if (!autoSort && isEditingTextField) { _PreviousTime = normalizedTime; } else { if (!float.IsNaN(_PreviousTime)) { if (Event.current.keyCode != KeyCode.Escape) { normalizedTime = _PreviousTime; AnimancerGUI.Deselect(); } _PreviousTime = float.NaN; } WrapEventTime(context, ref normalizedTime); timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, index, normalizedTime); if (autoSort) { SortEvents(context); } } GUI.changed = true; } } } else// Dummy End Event (when there are no event times). { AnimancerUtilities.Assert(index == 0, "Dummy end event index != 0"); EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(timeArea, GUIContent.none, context.Times.Property); AnimationTimeAttribute.nextDefaultValue = defaultTime; normalizedTime = float.NaN; EventTimeAttribute.OnGUI(timeArea, label, ref normalizedTime); EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck() && !float.IsNaN(normalizedTime)) { context.Times.Count = 1; var timeProperty = context.Times.GetElement(0); timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, 0, normalizedTime); } } } if (EditorGUI.EndChangeCheck()) { var eventType = Event.current.type; if (eventType == EventType.Layout) { return; } if (eventType == EventType.Used) { normalizedTime = UnitsAttribute.GetDisplayValue(normalizedTime, defaultTime); TransitionPreviewWindow.PreviewNormalizedTime = normalizedTime; } GUIUtility.ExitGUI(); } }
/************************************************************************************************************************/ private void DoChildPropertiesGUI(Rect area, SerializedProperty rootProperty, SerializedProperty mainProperty) { if (!rootProperty.isExpanded && _Mode != Mode.AlwaysExpanded) { return; } // Skip over the main property if it was already drawn by the header. if (rootProperty.propertyType == SerializedPropertyType.ManagedReference && mainProperty != null) { AnimancerGUI.NextVerticalArea(ref area); } EditorGUI.indentLevel++; var property = rootProperty.Copy(); SerializedProperty eventsProperty = null; var depth = property.depth; property.NextVisible(true); while (property.depth > depth) { // Grab the Events property and draw it last. var path = property.propertyPath; if (eventsProperty == null && path.EndsWith("._Events")) { eventsProperty = property.Copy(); } // Don't draw the main property again. else if (mainProperty != null && path.EndsWith(MainPropertyPathSuffix)) { } else { if (eventsProperty != null) { var type = Context.Transition.GetType(); var accessor = property.GetAccessor(); var field = Serialization.GetField(type, accessor.Name); if (field != null && field.IsDefined(typeof(DrawAfterEventsAttribute), false)) { using (ObjectPool.Disposable.AcquireContent(out var eventsLabel, eventsProperty)) DoChildPropertyGUI(ref area, rootProperty, eventsProperty, eventsLabel); AnimancerGUI.NextVerticalArea(ref area); eventsProperty = null; } } using (ObjectPool.Disposable.AcquireContent(out var label, property)) DoChildPropertyGUI(ref area, rootProperty, property, label); AnimancerGUI.NextVerticalArea(ref area); } if (!property.NextVisible(false)) { break; } } if (eventsProperty != null) { using (ObjectPool.Disposable.AcquireContent(out var label, eventsProperty)) DoChildPropertyGUI(ref area, rootProperty, eventsProperty, label); } EditorGUI.indentLevel--; }
/************************************************************************************************************************/ /// <summary>Draws the GUI fields for the event at the specified `index`.</summary> public static void DoCallbackGUI(ref Rect area, Context context, int index, bool autoSort, string callbackLabel) { EditorGUI.BeginChangeCheck(); using (ObjectPool.Disposable.AcquireContent(out var label, callbackLabel)) { if (index < context.Callbacks.Count) { var callback = context.Callbacks.GetElement(index); area.height = EditorGUI.GetPropertyHeight(callback, false); EditorGUI.BeginProperty(area, GUIContent.none, callback); // UnityEvents ignore the proper indentation which makes them look terrible in a list. // So we force the area to be indented. var indentedArea = EditorGUI.IndentedRect(area); var indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; EditorGUI.PropertyField(indentedArea, callback, label, false); EditorGUI.indentLevel = indentLevel; EditorGUI.EndProperty(); } else if (DummySerializableCallback.DoCallbackGUI(ref area, label, context.Callbacks.Property, out var callback)) { try { Sequence.DisableCompactArrays = true; if (index >= context.Times.Count) { context.Times.Property.InsertArrayElementAtIndex(index); context.Times.Count++; context.Times.GetElement(index).floatValue = float.NaN; context.Times.Property.serializedObject.ApplyModifiedProperties(); } context.Callbacks.Property.ForEachTarget((callbacksProperty) => { var accessor = callbacksProperty.GetAccessor(); var oldCallbacks = (Array)accessor.GetValue(callbacksProperty.serializedObject.targetObject); Array newCallbacks; if (oldCallbacks == null) { var elementType = accessor.GetFieldElementType(callbacksProperty); newCallbacks = Array.CreateInstance(elementType, 1); } else { var elementType = oldCallbacks.GetType().GetElementType(); newCallbacks = Array.CreateInstance(elementType, index + 1); Array.Copy(oldCallbacks, newCallbacks, oldCallbacks.Length); } newCallbacks.SetValue(callback, index); accessor.SetValue(callbacksProperty, newCallbacks); }); context.Callbacks.Property.OnPropertyChanged(); context.Callbacks.Refresh(); } finally { Sequence.DisableCompactArrays = false; } } } if (EditorGUI.EndChangeCheck()) { if (index < context.Callbacks.Count) { var events = context.Sequence?.InitializedEvents; if (events != null) { var animancerEvent = index < events.Count ? events[index] : events.endEvent; if (AnimancerEvent.IsNullOrDummy(animancerEvent.callback)) { context.Callbacks.Property.serializedObject.ApplyModifiedProperties(); var property = context.Callbacks.GetElement(index); var callback = property.GetValue(); var invoker = Sequence.GetInvoker(callback); if (index < events.Count) { events.SetCallback(index, invoker); } else { events.OnEnd = invoker; } } } } } AnimancerGUI.NextVerticalArea(ref area); }
/************************************************************************************************************************/ /// <summary>Draws the GUI for the `events`.</summary> public void Draw(ref Rect area, Sequence events, GUIContent label) { if (events == null) { return; } area.height = AnimancerGUI.LineHeight; var headerArea = area; const string LogLabel = "Log"; if (float.IsNaN(_LogButtonWidth)) { _LogButtonWidth = EditorStyles.miniButton.CalculateWidth(LogLabel); } var logArea = AnimancerGUI.StealFromRight(ref headerArea, _LogButtonWidth); if (GUI.Button(logArea, LogLabel, EditorStyles.miniButton)) { Debug.Log(events.DeepToString()); } _IsExpanded = EditorGUI.Foldout(headerArea, _IsExpanded, GUIContent.none, true); using (ObjectPool.Disposable.AcquireContent(out var summary, GetSummary(events))) EditorGUI.LabelField(headerArea, label, summary); AnimancerGUI.NextVerticalArea(ref area); if (!_IsExpanded) { return; } var enabled = GUI.enabled; GUI.enabled = false; EditorGUI.indentLevel++; for (int i = 0; i < events.Count; i++) { var name = events.GetName(i); if (string.IsNullOrEmpty(name)) { if (_EventNumberCache == null) { _EventNumberCache = new ConversionCache <int, string>((index) => $"Event {index}"); } name = _EventNumberCache.Convert(i); } Draw(ref area, name, events[i]); } Draw(ref area, "End Event", events.endEvent); EditorGUI.indentLevel--; GUI.enabled = enabled; }
/************************************************************************************************************************/ /// <summary>Draws the time field for the event at the specified `index`.</summary> public static void DoNameGUI(ref Rect area, Context context, int index, string nameLabel) { if (nameLabel == null) { return; } EditorGUI.BeginChangeCheck(); string name; area.height = AnimancerGUI.LineHeight; var fieldArea = area; AnimancerGUI.NextVerticalArea(ref area); using (ObjectPool.Disposable.AcquireContent(out var label, nameLabel, "An optional name which can be used to identify the event in code." + " Leaving all names blank is recommended if you are not using them.")) { fieldArea = EditorGUI.PrefixLabel(fieldArea, label); } var indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; if (index < context.Names.Count) { var nameProperty = context.Names.GetElement(index); EditorGUI.BeginProperty(fieldArea, GUIContent.none, nameProperty); name = nameProperty.stringValue = DoEventNameTextField(fieldArea, context, nameProperty.stringValue); EditorGUI.EndProperty(); } else { EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(fieldArea, GUIContent.none, context.Names.Property); name = DoEventNameTextField(fieldArea, context, ""); EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck() && !string.IsNullOrEmpty(name)) { context.Names.Count++; if (context.Names.Count < index + 1) { var nextProperty = context.Names.GetElement(context.Names.Count - 1); nextProperty.stringValue = ""; context.Names.Count = index + 1; } var nameProperty = context.Names.GetElement(index); nameProperty.stringValue = name; } } EditorGUI.indentLevel = indentLevel; if (EditorGUI.EndChangeCheck()) { var events = context.Sequence?.InitializedEvents; if (events != null) { events.SetName(index, name); } } }
/// <summary>Draws the time field for the event at the specified `index`.</summary> public static void DoEventTimeGUI(ref Rect area, Context context, int index, bool autoSort, out string callbackLabel) { EditorGUI.BeginChangeCheck(); area.height = AnimancerGUI.LineHeight; var timeArea = area; AnimancerGUI.NextVerticalArea(ref area); GUIContent timeLabel; float defaultTime; bool isEndEvent; GetEventLabels(index, context, out timeLabel, out callbackLabel, out defaultTime, out isEndEvent); var length = context.TransitionContext.MaximumDuration; float normalizedTime; if (index < context.TimeCount) { var timeProperty = context.GetTime(index); var wasEditingTextField = EditorGUIUtility.editingTextField; if (!wasEditingTextField) { _PreviousTime = float.NaN; } EditorGUI.BeginChangeCheck(); timeLabel = EditorGUI.BeginProperty(area, timeLabel, timeProperty); normalizedTime = AnimancerGUI.DoOptionalTimeField( ref timeArea, timeLabel, timeProperty.floatValue, true, length, defaultTime); EditorGUI.EndProperty(); var isEditingTextField = EditorGUIUtility.editingTextField; if (EditorGUI.EndChangeCheck() || (wasEditingTextField && !isEditingTextField)) { if (isEndEvent) { timeProperty.floatValue = normalizedTime; } else if (float.IsNaN(normalizedTime)) { RemoveEvent(context, index); AnimancerGUI.Deselect(); } else if (!autoSort && isEditingTextField) { _PreviousTime = normalizedTime; } else { if (!float.IsNaN(_PreviousTime)) { if (Event.current.keyCode != KeyCode.Escape) { normalizedTime = _PreviousTime; AnimancerGUI.Deselect(); } _PreviousTime = float.NaN; } WrapEventTime(context, ref normalizedTime); timeProperty.floatValue = normalizedTime; if (autoSort) { SortEvents(context); } } GUI.changed = true; } } else// Dummy End Event. { Debug.Assert(index == 0, "This is assumed to be a dummy end event, which should only be at index 0"); EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(timeArea, GUIContent.none, context.Times); normalizedTime = AnimancerGUI.DoOptionalTimeField( ref timeArea, timeLabel, float.NaN, true, length, defaultTime, true); EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck() && !float.IsNaN(normalizedTime)) { context.TimeCount = 1; var timeProperty = context.GetTime(0); timeProperty.floatValue = normalizedTime; } } if (EditorGUI.EndChangeCheck()) { TransitionPreviewWindow.SetPreviewNormalizedTime(normalizedTime); if (Event.current.type != EventType.Layout) { GUIUtility.ExitGUI(); } } }
/************************************************************************************************************************/ /// <summary>Draws the GUI fields for the event at the specified `index`.</summary> public static void DoEventGUI(ref Rect area, Context context, int index, bool autoSort, bool showCallback) { string callbackLabel; DoEventTimeGUI(ref area, context, index, autoSort, out callbackLabel); if (showCallback) { var label = AnimancerGUI.TempContent(callbackLabel); if (index < context.CallbackCount) { var callback = context.GetCallback(index); area.height = EditorGUI.GetPropertyHeight(callback, false); EditorGUI.BeginProperty(area, GUIContent.none, callback); // UnityEvents ignore the proper indentation which makes them look terrible in a list. // So we force the area to be indented. var indentedArea = EditorGUI.IndentedRect(area); var indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; EditorGUI.PropertyField(indentedArea, callback, label, false); EditorGUI.indentLevel = indentLevel; EditorGUI.EndProperty(); } else { var length = context.TransitionContext.MaximumDuration; object callback; if (DummySerializableCallback.DoCallbackGUI(ref area, label, context.Callbacks, out callback)) { context.Callbacks.RecordUndo(); context.Callbacks.ForEachTarget((callbacksProperty) => { var accessor = callbacksProperty.GetAccessor(); var oldCallbacks = (Array)accessor.GetValue(callbacksProperty.serializedObject.targetObject); Array newCallbacks; if (oldCallbacks == null) { var elementType = accessor.FieldType.GetElementType(); newCallbacks = Array.CreateInstance(elementType, 1); } else { var elementType = oldCallbacks.GetType().GetElementType(); newCallbacks = Array.CreateInstance(elementType, index + 1); Array.Copy(oldCallbacks, newCallbacks, oldCallbacks.Length); } newCallbacks.SetValue(callback, index); accessor.SetValue(callbacksProperty, newCallbacks); }); context.Callbacks.OnPropertyChanged(); context.CallbackCount = index + 1; if (index >= context.TimeCount) { context.Times.InsertArrayElementAtIndex(index); context.TimeCount++; context.GetTime(index).floatValue = float.NaN; } } } AnimancerGUI.NextVerticalArea(ref area); } }