public void EventLineGUI(Rect rect, AnimationWindowState state) { // We only display and manipulate animation events from the main // game object in selection. If we ever want to update to handle // a multiple selection, a single timeline might not be sufficient... AnimationClip clip = state.activeAnimationClip; GameObject animated = state.activeRootGameObject; GUI.BeginGroup(rect); Color backupCol = GUI.color; Rect eventLineRect = new Rect(0, 0, rect.width, rect.height); float mousePosTime = Mathf.Max(Mathf.RoundToInt(state.PixelToTime(Event.current.mousePosition.x, rect) * state.frameRate) / state.frameRate, 0.0f); // Draw events if (clip != null) { AnimationEvent[] events = AnimationUtility.GetAnimationEvents(clip); Texture eventMarker = EditorGUIUtility.IconContent("Animation.EventMarker").image; // Calculate rects Rect[] hitRects = new Rect[events.Length]; Rect[] drawRects = new Rect[events.Length]; int shared = 1; int sharedLeft = 0; for (int i = 0; i < events.Length; i++) { AnimationEvent evt = events[i]; if (sharedLeft == 0) { shared = 1; while (i + shared < events.Length && events[i + shared].time == evt.time) { shared++; } sharedLeft = shared; } sharedLeft--; // Important to take floor of positions of GUI stuff to get pixel correct alignment of // stuff drawn with both GUI and Handles/GL. Otherwise things are off by one pixel half the time. float keypos = Mathf.Floor(state.FrameToPixel(evt.time * clip.frameRate, rect)); int sharedOffset = 0; if (shared > 1) { float spread = Mathf.Min((shared - 1) * (eventMarker.width - 1), (int)(state.FrameDeltaToPixel(rect) - eventMarker.width * 2)); sharedOffset = Mathf.FloorToInt(Mathf.Max(0, spread - (eventMarker.width - 1) * (sharedLeft))); } Rect r = new Rect( keypos + sharedOffset - eventMarker.width / 2, (rect.height - 10) * (float)(sharedLeft - shared + 1) / Mathf.Max(1, shared - 1), eventMarker.width, eventMarker.height); hitRects[i] = r; drawRects[i] = r; } // Store tooptip info if (m_DirtyTooltip) { if (m_HoverEvent >= 0 && m_HoverEvent < hitRects.Length) { m_InstantTooltipText = AnimationWindowEventInspector.FormatEvent(animated, events[m_HoverEvent]); m_InstantTooltipPoint = new Vector2(hitRects[m_HoverEvent].xMin + (int)(hitRects[m_HoverEvent].width / 2) + rect.x - 30, rect.yMax); } m_DirtyTooltip = false; } bool[] selectedEvents = new bool[events.Length]; Object[] selectedObjects = Selection.objects; foreach (Object selectedObject in selectedObjects) { AnimationWindowEvent awe = selectedObject as AnimationWindowEvent; if (awe != null) { if (awe.eventIndex >= 0 && awe.eventIndex < selectedEvents.Length) { selectedEvents[awe.eventIndex] = true; } } } Vector2 offset = Vector2.zero; int clickedIndex; float startSelection, endSelection; // TODO: GUIStyle.none has hopping margins that need to be fixed HighLevelEvent hEvent = EditorGUIExt.MultiSelection( rect, drawRects, new GUIContent(eventMarker), hitRects, ref selectedEvents, null, out clickedIndex, out offset, out startSelection, out endSelection, GUIStyle.none ); if (hEvent != HighLevelEvent.None) { switch (hEvent) { case HighLevelEvent.BeginDrag: m_EventsAtMouseDown = events; m_EventTimes = new float[events.Length]; for (int i = 0; i < events.Length; i++) { m_EventTimes[i] = events[i].time; } break; case HighLevelEvent.SelectionChanged: state.ClearKeySelections(); EditEvents(animated, clip, selectedEvents); break; case HighLevelEvent.Delete: DeleteEvents(clip, selectedEvents); break; case HighLevelEvent.DoubleClick: if (clickedIndex != -1) { EditEvents(animated, clip, selectedEvents); } else { EventLineContextMenuAdd(new EventLineContextMenuObject(animated, clip, mousePosTime, -1, selectedEvents)); } break; case HighLevelEvent.Drag: for (int i = events.Length - 1; i >= 0; i--) { if (selectedEvents[i]) { AnimationEvent evt = m_EventsAtMouseDown[i]; evt.time = m_EventTimes[i] + offset.x * state.PixelDeltaToTime(rect); evt.time = Mathf.Max(0.0F, evt.time); evt.time = Mathf.RoundToInt(evt.time * clip.frameRate) / clip.frameRate; } } int[] order = new int[selectedEvents.Length]; for (int i = 0; i < order.Length; i++) { order[i] = i; } System.Array.Sort(m_EventsAtMouseDown, order, new EventComparer()); bool[] selectedOld = (bool[])selectedEvents.Clone(); float[] timesOld = (float[])m_EventTimes.Clone(); for (int i = 0; i < order.Length; i++) { selectedEvents[i] = selectedOld[order[i]]; m_EventTimes[i] = timesOld[order[i]]; } // Update selection to reflect new order. EditEvents(animated, clip, selectedEvents); Undo.RegisterCompleteObjectUndo(clip, "Move Event"); AnimationUtility.SetAnimationEvents(clip, m_EventsAtMouseDown); m_DirtyTooltip = true; break; case HighLevelEvent.ContextClick: GenericMenu menu = new GenericMenu(); var contextData = new EventLineContextMenuObject(animated, clip, events[clickedIndex].time, clickedIndex, selectedEvents); int selectedEventsCount = selectedEvents.Count(selected => selected); menu.AddItem( EditorGUIUtility.TrTextContent("Add Animation Event"), false, EventLineContextMenuAdd, contextData); menu.AddItem( new GUIContent(selectedEventsCount > 1 ? "Delete Animation Events" : "Delete Animation Event"), false, EventLineContextMenuDelete, contextData); menu.ShowAsContext(); // Mouse may move while context menu is open - make sure instant tooltip is handled m_InstantTooltipText = null; m_DirtyTooltip = true; state.Repaint(); break; } } CheckRectsOnMouseMove(rect, events, hitRects); // Create context menu on context click if (Event.current.type == EventType.ContextClick && eventLineRect.Contains(Event.current.mousePosition)) { Event.current.Use(); // Create menu GenericMenu menu = new GenericMenu(); var contextData = new EventLineContextMenuObject(animated, clip, mousePosTime, -1, selectedEvents); int selectedEventsCount = selectedEvents.Count(selected => selected); menu.AddItem( EditorGUIUtility.TrTextContent("Add Animation Event"), false, EventLineContextMenuAdd, contextData); if (selectedEventsCount > 0) { menu.AddItem( new GUIContent(selectedEventsCount > 1 ? "Delete Animation Events" : "Delete Animation Event"), false, EventLineContextMenuDelete, contextData); } menu.ShowAsContext(); } } GUI.color = backupCol; GUI.EndGroup(); }
void SliderGUI() { if (!m_HSlider && !m_VSlider) { return; } using (new EditorGUI.DisabledScope(!enableMouseInput)) { Bounds editorBounds = drawingBounds; Rect area = shownAreaInsideMargins; float min, max; float inset = styles.sliderWidth - styles.visualSliderWidth; float otherInset = (vSlider && hSlider) ? inset : 0; Vector2 scaleDelta = m_Scale; // Horizontal range slider if (m_HSlider) { Rect hRangeSliderRect = new Rect(drawRect.x + 1, drawRect.yMax - inset, drawRect.width - otherInset, styles.sliderWidth); float shownXRange = area.width; float shownXMin = area.xMin; if (allowSliderZoomHorizontal) { EditorGUIExt.MinMaxScroller(hRangeSliderRect, horizontalScrollbarID, ref shownXMin, ref shownXRange, editorBounds.min.x, editorBounds.max.x, Mathf.NegativeInfinity, Mathf.Infinity, styles.horizontalScrollbar, styles.horizontalMinMaxScrollbarThumb, styles.horizontalScrollbarLeftButton, styles.horizontalScrollbarRightButton, true); } else { shownXMin = GUI.Scroller(hRangeSliderRect, shownXMin, shownXRange, editorBounds.min.x, editorBounds.max.x, styles.horizontalScrollbar, styles.horizontalMinMaxScrollbarThumb, styles.horizontalScrollbarLeftButton, styles.horizontalScrollbarRightButton, true); } min = shownXMin; max = shownXMin + shownXRange; float rectWidthWithinMargins = GetWidthInsideMargins(rect.width, true); if (min > area.xMin) { min = Mathf.Min(min, max - rectWidthWithinMargins / m_HScaleMax); } if (max < area.xMax) { max = Mathf.Max(max, min + rectWidthWithinMargins / m_HScaleMax); } SetShownHRangeInsideMargins(min, max); } // Vertical range slider // Reverse y values since y increses upwards for the draw area but downwards for the slider if (m_VSlider) { if (m_UpDirection == YDirection.Positive) { Rect vRangeSliderRect = new Rect(drawRect.xMax - inset, drawRect.y, styles.sliderWidth, drawRect.height - otherInset); float shownYRange = area.height; float shownYMin = -area.yMax; if (allowSliderZoomVertical) { EditorGUIExt.MinMaxScroller(vRangeSliderRect, verticalScrollbarID, ref shownYMin, ref shownYRange, -editorBounds.max.y, -editorBounds.min.y, Mathf.NegativeInfinity, Mathf.Infinity, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } else { shownYMin = GUI.Scroller(vRangeSliderRect, shownYMin, shownYRange, -editorBounds.max.y, -editorBounds.min.y, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } min = -(shownYMin + shownYRange); max = -shownYMin; float rectHeightWithinMargins = GetHeightInsideMargins(rect.height, true); if (min > area.yMin) { min = Mathf.Min(min, max - rectHeightWithinMargins / m_VScaleMax); } if (max < area.yMax) { max = Mathf.Max(max, min + rectHeightWithinMargins / m_VScaleMax); } SetShownVRangeInsideMargins(min, max); } else { Rect vRangeSliderRect = new Rect(drawRect.xMax - inset, drawRect.y, styles.sliderWidth, drawRect.height - otherInset); float shownYRange = area.height; float shownYMin = area.yMin; if (allowSliderZoomVertical) { EditorGUIExt.MinMaxScroller(vRangeSliderRect, verticalScrollbarID, ref shownYMin, ref shownYRange, editorBounds.min.y, editorBounds.max.y, Mathf.NegativeInfinity, Mathf.Infinity, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } else { shownYMin = GUI.Scroller(vRangeSliderRect, shownYMin, shownYRange, editorBounds.min.y, editorBounds.max.y, styles.verticalScrollbar, styles.verticalMinMaxScrollbarThumb, styles.verticalScrollbarUpButton, styles.verticalScrollbarDownButton, false); } min = shownYMin; max = shownYMin + shownYRange; float rectHeightWithinMargins = GetHeightInsideMargins(rect.height, true); if (min > area.yMin) { min = Mathf.Min(min, max - rectHeightWithinMargins / m_VScaleMax); } if (max < area.yMax) { max = Mathf.Max(max, min + rectHeightWithinMargins / m_VScaleMax); } SetShownVRangeInsideMargins(min, max); } } if (uniformScale) { float aspect = drawRect.width / drawRect.height; scaleDelta -= m_Scale; var delta = new Vector2(-scaleDelta.y * aspect, -scaleDelta.x / aspect); m_Scale -= delta; m_Translation.x -= scaleDelta.y / 2; m_Translation.y -= scaleDelta.x / 2; EnforceScaleAndRange(); } } }