/// <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); }
/// <summary> /// Draws the details of the <see cref="EventSequenceDrawer.Context.Callbacks"/>. /// </summary> public void DoEventsGUI(EventSequenceDrawer.Context context, out float addEventNormalizedTime) { addEventNormalizedTime = float.NaN; var currentGUIEvent = Event.current; EventControlIDs.Clear(); var selectedEventControlID = -1; var baseControlID = GUIUtility.GetControlID(EventHash - 1, FocusType.Passive); for (int i = 0; i < EventTimes.Count; i++) { var controlID = GUIUtility.GetControlID(EventHash + i, FocusType.Keyboard); EventControlIDs.Add(controlID); if (context.SelectedEvent == i) { selectedEventControlID = controlID; } } EventControlIDs.Add(baseControlID); switch (currentGUIEvent.type) { case EventType.Repaint: RepaintEventsGUI(context); break; case EventType.MouseDown: if (_Area.Contains(currentGUIEvent.mousePosition)) { OnMouseDown(currentGUIEvent, context, out addEventNormalizedTime); } break; case EventType.MouseDrag: if (GUIUtility.hotControl == baseControlID) { SetPreviewTime(currentGUIEvent); GUIUtility.ExitGUI(); } break; case EventType.KeyUp: if (GUIUtility.keyboardControl == selectedEventControlID && (currentGUIEvent.keyCode == KeyCode.Delete || currentGUIEvent.keyCode == KeyCode.Backspace)) { EventSequenceDrawer.RemoveEvent(context, context.SelectedEvent); GUIUtility.ExitGUI(); } break; } }
/// <summary>Returns a cached <see cref="EventSequenceDrawer"/> for the `events`.</summary> /// <remarks> /// The cache uses a <see cref="ConditionalWeakTable{TKey, TValue}"/> so it doesn't prevent the `events` /// from being garbage collected. /// </remarks> public static EventSequenceDrawer Get(Sequence events) { if (events == null) { return(null); } if (!SequenceToDrawer.TryGetValue(events, out var drawer)) { SequenceToDrawer.Add(events, drawer = new EventSequenceDrawer()); } return(drawer); }
/************************************************************************************************************************/ /// <summary> /// Calculates the number of vertical pixels the `property` will occupy when it is drawn. /// </summary> public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { using (var context = Context.Get(property)) { var height = AnimancerGUI.LineHeight; var count = Math.Max(1, context.Times.Count); for (int i = 0; i < count; i++) { height += CalculateEventHeight(context, i) * GetVisibility(context, i).faded; } var events = context.Sequence?.InitializedEvents; if (events != null) { height += EventSequenceDrawer.Get(events).CalculateHeight(events) + AnimancerGUI.StandardSpacing; } return(height); } }
/************************************************************************************************************************/ private void ShowContextMenu(Event currentGUIEvent, EventSequenceDrawer.Context context) { context = context.Copy(); var time = SecondsToNormalized(PixelsToSeconds(currentGUIEvent.mousePosition.x)); var hasSelectedEvent = context.SelectedEvent >= 0; var menu = new GenericMenu(); AddContextFunction(menu, context, "Add Event (Double Click)", true, () => EventSequenceDrawer.AddEvent(context, time)); AddContextFunction(menu, context, "Remove Event (Delete)", hasSelectedEvent, () => EventSequenceDrawer.RemoveEvent(context, context.SelectedEvent)); const string NudgePrefix = "Nudge Event Time/"; AddContextFunction(menu, context, NudgePrefix + "Left 1 Pixel (Left Arrow)", hasSelectedEvent, () => NudgeEventTime(context, -1)); AddContextFunction(menu, context, NudgePrefix + "Left 10 Pixels (Shift + Left Arrow)", hasSelectedEvent, () => NudgeEventTime(context, -10)); AddContextFunction(menu, context, NudgePrefix + "Right 1 Pixel (Right Arrow)", hasSelectedEvent, () => NudgeEventTime(context, 1)); AddContextFunction(menu, context, NudgePrefix + "Right 10 Pixels (Shift + Right Arrow)", hasSelectedEvent, () => NudgeEventTime(context, 10)); var canRoundTime = hasSelectedEvent; if (canRoundTime) { time = context.Times.GetElement(context.SelectedEvent).floatValue; canRoundTime = TryRoundValue(ref time); } AddContextFunction(menu, context, $"Round Event Time to {time}x (Space)", canRoundTime, () => RoundEventTime(context)); menu.ShowAsContext(); }
/************************************************************************************************************************/ /// <summary>Draws the GUI for the `property`.</summary> public override void OnGUI(Rect area, SerializedProperty property, GUIContent label) { using (var context = Context.Get(property)) { DoHeaderGUI(ref area, label, context); if (!property.hasMultipleDifferentValues) { EditorGUI.indentLevel++; DoAllEventsGUI(ref area, context); EditorGUI.indentLevel--; } var sequence = context.Sequence?.InitializedEvents; if (sequence != null) { using (ObjectPool.Disposable.AcquireContent(out label, "Runtime Events", $"The runtime {nameof(AnimancerEvent)}.{nameof(Sequence)} created from the serialized data above")) { EventSequenceDrawer.Get(sequence).Draw(ref area, sequence, label); } } } }
/// <summary> /// Draws the details of the <see cref="EventSequenceDrawer.Context.Callbacks"/>. /// </summary> public void DoEventsGUI(EventSequenceDrawer.Context context, out float addEventNormalizedTime) { addEventNormalizedTime = float.NaN; var currentGUIEvent = Event.current; EventControlIDs.Clear(); var selectedEventControlID = -1; var baseControlID = GUIUtility.GetControlID(EventHash - 1, FocusType.Passive); for (int i = 0; i < EventTimes.Count; i++) { var controlID = GUIUtility.GetControlID(EventHash + i, FocusType.Keyboard); EventControlIDs.Add(controlID); if (context.SelectedEvent == i) { selectedEventControlID = controlID; } } EventControlIDs.Add(baseControlID); switch (currentGUIEvent.type) { case EventType.Repaint: RepaintEventsGUI(context); break; case EventType.MouseDown: if (_Area.Contains(currentGUIEvent.mousePosition)) { OnMouseDown(currentGUIEvent, context, out addEventNormalizedTime); } break; case EventType.MouseUp: if (currentGUIEvent.button == 1 && _Area.Contains(currentGUIEvent.mousePosition)) { ShowContextMenu(currentGUIEvent, context); currentGUIEvent.Use(); } break; case EventType.MouseDrag: if (_Duration <= 0) { break; } var hotControl = GUIUtility.hotControl; if (hotControl == baseControlID) { SetPreviewTime(currentGUIEvent); GUIUtility.ExitGUI(); } else { for (int i = 0; i < EventTimes.Count; i++) { if (hotControl == EventControlIDs[i]) { if (context.Times.Count < 1) { context.Times.Count = 1; } var timeProperty = context.Times.GetElement(i); var seconds = PixelsToSeconds(currentGUIEvent.mousePosition.x); timeProperty.floatValue = seconds / _Duration; timeProperty.serializedObject.ApplyModifiedProperties(); GUIUtility.hotControl = EventControlIDs[context.SelectedEvent]; GUI.changed = true; SetPreviewTime(currentGUIEvent); GUIUtility.ExitGUI(); } } } break; case EventType.KeyUp: if (GUIUtility.keyboardControl != selectedEventControlID) { break; } var exitGUI = false; switch (currentGUIEvent.keyCode) { case KeyCode.Delete: case KeyCode.Backspace: EventSequenceDrawer.RemoveEvent(context, context.SelectedEvent); exitGUI = true; break; case KeyCode.LeftArrow: NudgeEventTime(context, Event.current.shift ? -10 : -1); break; case KeyCode.RightArrow: NudgeEventTime(context, Event.current.shift ? 10 : 1); break; case KeyCode.Space: RoundEventTime(context); break; default: return; // Don't call Use. } currentGUIEvent.Use(); GUI.changed = true; if (exitGUI) { GUIUtility.ExitGUI(); } break; } }
/// <summary> /// Draws the details of the <see cref="EventSequenceDrawer.Context.Callbacks"/>. /// </summary> public void DoEventsGUI(EventSequenceDrawer.Context context, out float addEventNormalizedTime) { addEventNormalizedTime = float.NaN; var currentGUIEvent = Event.current; EventControlIDs.Clear(); var selectedEventControlID = -1; var baseControlID = GUIUtility.GetControlID(EventHash - 1, FocusType.Passive); for (int i = 0; i < EventTimes.Count; i++) { var controlID = GUIUtility.GetControlID(EventHash + i, FocusType.Keyboard); EventControlIDs.Add(controlID); if (context.SelectedEvent == i) { selectedEventControlID = controlID; } } EventControlIDs.Add(baseControlID); switch (currentGUIEvent.type) { case EventType.Repaint: RepaintEventsGUI(context); break; case EventType.MouseDown: if (_Area.Contains(currentGUIEvent.mousePosition)) { OnMouseDown(currentGUIEvent, context, out addEventNormalizedTime); } break; case EventType.MouseDrag: if (GUIUtility.hotControl == baseControlID) { SetPreviewTime(currentGUIEvent); GUIUtility.ExitGUI(); } break; case EventType.KeyUp: if (GUIUtility.keyboardControl != selectedEventControlID) { break; } var exitGUI = false; switch (currentGUIEvent.keyCode) { case KeyCode.Delete: case KeyCode.Backspace: EventSequenceDrawer.RemoveEvent(context, context.SelectedEvent); exitGUI = true; break; case KeyCode.LeftArrow: NudgeEventTime(context, false); break; case KeyCode.RightArrow: NudgeEventTime(context, true); break; case KeyCode.Space: RoundEventTime(context); break; default: return; // Don't call Use. } currentGUIEvent.Use(); GUI.changed = true; if (exitGUI) { GUIUtility.ExitGUI(); } break; } }