// NB: if draw is true, it'll draw stuff only, otherwise it'll handle events only. void LayoutEvent(Rect rect, AnimEvent animEvent, AnimEventLayoutData layoutData, bool draw) { // check if it's visible on timeline if (layoutData.start > rect.xMax || layoutData.end < rect.xMin) { return; } float heightOffset = (layoutData.heightOffset * TIMELINE_EVENT_HEIGHT); Rect eventRect = new Rect(layoutData.start, heightOffset, 0, TIMELINE_EVENT_HEIGHT); Rect labelRect = new Rect(layoutData.start + 2, heightOffset, layoutData.textWidth, TIMELINE_EVENT_HEIGHT - 2); if (draw) { LayoutEventVisuals(animEvent, layoutData.selected, eventRect, layoutData.text, labelRect); } else { LayoutEventGuiEvents(rect, animEvent, layoutData.selected, eventRect, layoutData.text, labelRect); } }
void LayoutEvents(Rect rect) { Event e = Event.current; if (e.type == EventType.mouseDown && e.button == 0 && rect.Contains(e.mousePosition) && m_playing == false) { // Move timeline m_animTime = SnapTimeToFrameRate(GuiPosToAnimTime(rect, e.mousePosition.x)); } if (e.type == EventType.MouseDown && e.button == 0 && e.clickCount == 2 && rect.Contains(e.mousePosition)) { // Double click for new event at that time // New event InsertEvent(GuiPosToAnimTime(rect, e.mousePosition.x), true); e.Use(); } GUI.BeginGroup(rect); if (m_events.Count == 0) { GUI.Label(new Rect(0, 0, rect.width, rect.height), "Double click to insert event", EditorStyles.centeredGreyMiniLabel); } // Layout events. This is done in 4 stages so that selected items are drawn on top of (after) non-selected ones, but have their gui events handled first. // Calc some metadata about each event (start/end position on timeline, etc). This is stored in a temporary array, parallel to events AnimEventLayoutData[] eventTimelineData = new AnimEventLayoutData[m_events.Count]; // First loop over and calculate start/end positions of events for (int i = 0; i < m_events.Count; ++i) { AnimEvent animEvent = m_events[i]; AnimEventLayoutData eventData = new AnimEventLayoutData(); eventTimelineData[i] = eventData; eventData.start = AnimTimeToGuiPos(rect, SnapTimeToFrameRate(animEvent.m_time)); eventData.text = animEvent.m_functionName; //.Replace(ANIM_EVENT_PREFIX,null); eventData.textWidth = Styles.TIMELINE_EVENT_TEXT.CalcSize(new GUIContent(eventData.text)).x; eventData.end = eventData.start + eventData.textWidth + 4; eventData.selected = m_selectedEvents.Contains(m_events[i]); } int maxEventOffset = 0; // Now loop over events and calculate the vertical offset of events so that they don't overlap for (int i = 0; i < m_events.Count; ++i) { // Store the offset of everything we're overlapping with in a mask, so we can get the first available offset. int usedOffsetsMask = 0; AnimEventLayoutData data = eventTimelineData[i]; for (int j = i - 1; j >= 0; --j) { // check for overlap of items before this one. A AnimEventLayoutData other = eventTimelineData[j]; if ((data.start > other.end || data.end < other.start) == false) { // overlaps! usedOffsetsMask |= 1 << (other.heightOffset); } } // Loop through mask to find first available offset. while (data.heightOffset < 32 && (usedOffsetsMask & (1 << data.heightOffset)) != 0) { data.heightOffset++; } if (data.heightOffset > maxEventOffset) { maxEventOffset = data.heightOffset; } } // Draw vertical lines where there's an event for (int i = 0; i < m_events.Count; ++i) { DrawRect(new Rect(eventTimelineData[i].start, 0, 1, rect.height), Color.grey); } // First draw events for (int i = 0; i < m_events.Count; ++i) { LayoutEvent(rect, m_events[i], eventTimelineData[i], true); } // Then handle gui events in reverse order for (int i = m_events.Count - 1; i >= 0; --i) { LayoutEvent(rect, m_events[i], eventTimelineData[i], false); } GUI.EndGroup(); // Draw selection rect if (m_dragState == eDragState.SelectEvent && Mathf.Abs(m_selectionMouseStart - e.mousePosition.x) > 1.0f) { // Draw selection rect Rect selectionRect = new Rect(rect) { xMin = Mathf.Min(m_selectionMouseStart, e.mousePosition.x), xMax = Mathf.Max(m_selectionMouseStart, e.mousePosition.x) }; DrawRect(selectionRect, COLOR_UNITY_BLUE.WithAlpha(0.1f), COLOR_UNITY_BLUE.WithAlpha(0.6f)); } // Check for unhandled mouse left click. It should deselect any selected events if (e.type == EventType.MouseDown && e.button == 0) { if (rect.Contains(e.mousePosition)) { ClearSelection(); e.Use(); } } // Check for unhanlded drag, it should start a select if (m_dragState == eDragState.None && e.type == EventType.MouseDrag && e.button == 0) { if (rect.Contains(e.mousePosition)) { m_dragState = eDragState.SelectEvent; e.Use(); } } if (m_dragState == eDragState.MoveEvent) { // While moving frame, show the move cursor EditorGUIUtility.AddCursorRect(rect, MouseCursor.MoveArrow); if (e.type == EventType.MouseDrag) { MoveSelectedEvents(e.delta.x); // move the frame e.Use(); } if (e.rawType == EventType.MouseUp && e.button == 0) { // Apply the move change ApplyChanges(); m_dragState = eDragState.None; e.Use(); } } float newTimelineHeight = Mathf.Max((maxEventOffset + 1), 1.5f) * TIMELINE_EVENT_HEIGHT; if (newTimelineHeight != m_timelineEventBarHeight) { m_timelineEventBarHeight = newTimelineHeight; Repaint(); } }