470 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Linq;
 | |
| 
 | |
| #if UNITY_2021_2_OR_NEWER
 | |
| using UnityEditor.SceneManagement;
 | |
| #else
 | |
| using UnityEditor.Experimental.SceneManagement;
 | |
| #endif
 | |
| using UnityEngine;
 | |
| using UnityEngine.Playables;
 | |
| using UnityEngine.Timeline;
 | |
| 
 | |
| namespace UnityEditor.Timeline
 | |
| {
 | |
|     partial class TimelineWindow
 | |
|     {
 | |
|         struct MarkerOverlay
 | |
|         {
 | |
|             public IMarker marker;
 | |
|             public Rect rect;
 | |
|             public bool isSelected;
 | |
|             public bool isCollapsed;
 | |
|             public MarkerEditor editor;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         enum TimelineItemArea
 | |
|         {
 | |
|             Header,
 | |
|             Lines
 | |
|         }
 | |
| 
 | |
|         static internal readonly TimelineMode s_ActiveMode = new TimelineActiveMode();
 | |
|         static internal readonly TimelineMode s_EditAssetMode = new TimelineAssetEditionMode();
 | |
|         static internal readonly TimelineMode s_InactiveMode = new TimelineInactiveMode();
 | |
|         static internal readonly TimelineMode s_DisabledMode = new TimelineDisabledMode();
 | |
|         static internal readonly TimelineMode s_PrefabOutOfContextMode = new TimelineAssetEditionMode();
 | |
|         static internal readonly TimelineMode s_ReadonlyMode = new TimelineReadOnlyMode();
 | |
| 
 | |
|         private static readonly GUIContent MenuItemFrames = L10n.TextContent("Frames");
 | |
|         private static readonly GUIContent MenuItemTimecode = L10n.TextContent("Timecode");
 | |
|         private static readonly GUIContent MenuItemSeconds = L10n.TextContent("Seconds");
 | |
| 
 | |
|         static readonly string k_FrameRateMenuLabel = L10n.Tr("Frame Rate/{0}");
 | |
|         static readonly string k_CustomFpsLabel = L10n.Tr("{0}: {1:f2} fps");
 | |
| 
 | |
|         float m_VerticalScrollBarSize, m_HorizontalScrollBarSize;
 | |
| 
 | |
|         List<MarkerOverlay> m_OverlayQueue = new List<MarkerOverlay>(100);
 | |
| 
 | |
|         float headerHeight
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return WindowConstants.markerRowYPosition + (state.showMarkerHeader ? WindowConstants.markerRowHeight : 0.0f);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public Rect markerHeaderRect
 | |
|         {
 | |
|             get { return new Rect(0.0f, WindowConstants.markerRowYPosition, state.sequencerHeaderWidth, WindowConstants.markerRowHeight); }
 | |
|         }
 | |
| 
 | |
|         public Rect markerContentRect
 | |
|         {
 | |
|             get { return Rect.MinMaxRect(state.sequencerHeaderWidth, WindowConstants.markerRowYPosition, position.width, WindowConstants.markerRowYPosition + WindowConstants.markerRowHeight); }
 | |
|         }
 | |
| 
 | |
|         Rect trackRect
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 var yMinHeight = headerHeight;
 | |
|                 return new Rect(0, yMinHeight, position.width, position.height - yMinHeight - horizontalScrollbarHeight);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public Rect sequenceRect
 | |
|         {
 | |
|             get { return new Rect(0.0f, WindowConstants.markerRowYPosition, position.width - WindowConstants.sliderWidth, position.height - WindowConstants.timeAreaYPosition); }
 | |
|         }
 | |
| 
 | |
|         public Rect sequenceHeaderRect
 | |
|         {
 | |
|             get { return new Rect(0.0f, WindowConstants.markerRowYPosition, state.sequencerHeaderWidth, position.height - WindowConstants.timeAreaYPosition); }
 | |
|         }
 | |
| 
 | |
|         public Rect headerSplitterRect
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return new Rect(state.sequencerHeaderWidth - WindowConstants.headerSplitterWidth / 2.0f, WindowConstants.timeAreaYPosition, WindowConstants.headerSplitterWidth, clientArea.height);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public Rect sequenceContentRect
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return new Rect(
 | |
|                     state.sequencerHeaderWidth,
 | |
|                     WindowConstants.markerRowYPosition,
 | |
|                     position.width - state.sequencerHeaderWidth - (treeView != null && treeView.showingVerticalScrollBar ? WindowConstants.sliderWidth : 0),
 | |
|                     position.height - WindowConstants.markerRowYPosition - horizontalScrollbarHeight);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public float verticalScrollbarWidth
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return m_VerticalScrollBarSize;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public float horizontalScrollbarHeight
 | |
|         {
 | |
|             get { return m_HorizontalScrollBarSize; }
 | |
|         }
 | |
| 
 | |
|         internal TimelineMode currentMode
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (state == null || state.editSequence.asset == null)
 | |
|                     return s_InactiveMode;
 | |
|                 if (state.editSequence.isReadOnly)
 | |
|                     return s_ReadonlyMode;
 | |
|                 if (state.editSequence.director == null || state.masterSequence.director == null)
 | |
|                     return s_EditAssetMode;
 | |
| 
 | |
|                 if (PrefabUtility.IsPartOfPrefabAsset(state.editSequence.director))
 | |
|                 {
 | |
|                     var stage = PrefabStageUtility.GetCurrentPrefabStage();
 | |
|                     if (stage == null || !stage.IsPartOfPrefabContents(state.editSequence.director.gameObject))
 | |
|                         return s_PrefabOutOfContextMode;
 | |
|                 }
 | |
| 
 | |
|                 if (!state.masterSequence.director.isActiveAndEnabled)
 | |
|                     return s_DisabledMode;
 | |
| 
 | |
|                 return s_ActiveMode;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void DoLayout()
 | |
|         {
 | |
|             EventType rawType = Event.current.rawType;
 | |
|             var mousePosition = Event.current.mousePosition; // mousePosition is also affected by this bug and does not reflect the original position after a Use()
 | |
| 
 | |
|             Initialize();
 | |
| 
 | |
|             var processManipulators = Event.current.type != EventType.Repaint && Event.current.type != EventType.Layout;
 | |
| 
 | |
|             if (processManipulators)
 | |
|             {
 | |
|                 // Update what's under mouse the cursor
 | |
|                 PickerUtils.DoPick(state, mousePosition);
 | |
| 
 | |
|                 if (state.editSequence.asset != null)
 | |
|                     m_PreTreeViewControl.HandleManipulatorsEvents(state);
 | |
|             }
 | |
| 
 | |
|             SequencerGUI();
 | |
| 
 | |
|             if (processManipulators)
 | |
|             {
 | |
|                 if (state.editSequence.asset != null)
 | |
|                     m_PostTreeViewControl.HandleManipulatorsEvents(state);
 | |
|             }
 | |
| 
 | |
|             if (state.editSequence.asset != null)
 | |
|             {
 | |
|                 m_RectangleSelect.OnGUI(state, rawType, mousePosition);
 | |
|                 m_RectangleZoom.OnGUI(state, rawType, mousePosition);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void SplitterGUI()
 | |
|         {
 | |
|             if (!state.IsEditingAnEmptyTimeline())
 | |
|             {
 | |
|                 var splitterVisualRect = new Rect(state.sequencerHeaderWidth - WindowConstants.headerSplitterWidth / 2.0f,
 | |
|                     WindowConstants.timeAreaYPosition + 2.0f,
 | |
|                     WindowConstants.headerSplitterVisualWidth,
 | |
|                     clientArea.height);
 | |
| 
 | |
|                 EditorGUI.DrawRect(splitterVisualRect, DirectorStyles.Instance.customSkin.colorTopOutline3);
 | |
| 
 | |
|                 if (GUIUtility.hotControl == 0)
 | |
|                     EditorGUIUtility.AddCursorRect(headerSplitterRect, MouseCursor.SplitResizeLeftRight);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void TrackViewsGUI()
 | |
|         {
 | |
|             using (new GUIViewportScope(trackRect))
 | |
|             {
 | |
|                 TracksGUI(trackRect, state, currentMode.TrackState(state));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void UserOverlaysGUI()
 | |
|         {
 | |
|             if (Event.current.type != EventType.Repaint)
 | |
|                 return;
 | |
| 
 | |
|             if (m_OverlayQueue.Count == 0)
 | |
|                 return;
 | |
| 
 | |
|             // the rect containing the time area plus the time ruler
 | |
|             var screenRect = new Rect(
 | |
|                 state.sequencerHeaderWidth,
 | |
|                 WindowConstants.timeAreaYPosition,
 | |
|                 position.width - state.sequencerHeaderWidth - (treeView != null && treeView.showingVerticalScrollBar ? WindowConstants.sliderWidth : 0),
 | |
|                 position.height - WindowConstants.timeAreaYPosition - horizontalScrollbarHeight);
 | |
| 
 | |
|             var trackOffset = trackRect.y - screenRect.y;
 | |
|             var startTime = state.PixelToTime(screenRect.xMin);
 | |
|             var endTime = state.PixelToTime(screenRect.xMax);
 | |
| 
 | |
|             using (new GUIViewportScope(screenRect))
 | |
|             {
 | |
|                 foreach (var entry in m_OverlayQueue)
 | |
|                 {
 | |
|                     var uiState = MarkerUIStates.None;
 | |
|                     if (entry.isCollapsed)
 | |
|                         uiState |= MarkerUIStates.Collapsed;
 | |
|                     if (entry.isSelected)
 | |
|                         uiState |= MarkerUIStates.Selected;
 | |
|                     var region = new MarkerOverlayRegion(GUIClip.Clip(entry.rect), screenRect, startTime, endTime, trackOffset);
 | |
|                     try
 | |
|                     {
 | |
|                         entry.editor.DrawOverlay(entry.marker, uiState, region);
 | |
|                     }
 | |
|                     catch (Exception e)
 | |
|                     {
 | |
|                         Debug.LogException(e);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             m_OverlayQueue.Clear();
 | |
|         }
 | |
| 
 | |
|         void DrawHeaderBackground()
 | |
|         {
 | |
|             var rect = state.timeAreaRect;
 | |
|             rect.xMin = 0.0f;
 | |
|             EditorGUI.DrawRect(rect, DirectorStyles.Instance.customSkin.colorTimelineBackground);
 | |
|         }
 | |
| 
 | |
|         void HandleBottomFillerDragAndDrop(Rect rect)
 | |
|         {
 | |
|             if (Event.current.type != EventType.DragUpdated &&
 | |
|                 Event.current.type != EventType.DragExited &&
 | |
|                 Event.current.type != EventType.DragPerform)
 | |
|                 return;
 | |
| 
 | |
|             if (instance.treeView == null || instance.treeView.timelineDragging == null)
 | |
|                 return;
 | |
| 
 | |
|             if (!rect.Contains(Event.current.mousePosition))
 | |
|                 return;
 | |
| 
 | |
|             instance.treeView.timelineDragging.DragElement(null, new Rect(), -1);
 | |
|         }
 | |
| 
 | |
|         void DrawHeaderBackgroundBottomFiller()
 | |
|         {
 | |
|             var rect = sequenceRect;
 | |
|             rect.yMin = rect.yMax;
 | |
|             rect.yMax = rect.yMax + WindowConstants.sliderWidth;
 | |
|             if (state.editSequence.asset != null && !state.IsEditingAnEmptyTimeline())
 | |
|             {
 | |
|                 rect.width = state.sequencerHeaderWidth;
 | |
|             }
 | |
|             using (new GUIViewportScope(rect))
 | |
|             {
 | |
|                 Graphics.DrawBackgroundRect(state, rect);
 | |
|             }
 | |
| 
 | |
|             HandleBottomFillerDragAndDrop(rect);
 | |
|         }
 | |
| 
 | |
|         void SequencerGUI()
 | |
|         {
 | |
|             var duration = state.editSequence.duration;
 | |
| 
 | |
|             DrawHeaderBackground();
 | |
|             DurationGUI(TimelineItemArea.Header, duration);
 | |
| 
 | |
|             DrawToolbar();
 | |
| 
 | |
|             TrackViewsGUI();
 | |
|             MarkerHeaderGUI();
 | |
|             UserOverlaysGUI();
 | |
| 
 | |
|             DurationGUI(TimelineItemArea.Lines, duration);
 | |
|             PlayRangeGUI(TimelineItemArea.Lines);
 | |
|             TimeCursorGUI(TimelineItemArea.Lines);
 | |
|             DrawHeaderBackgroundBottomFiller();
 | |
| 
 | |
|             SubTimelineRangeGUI();
 | |
| 
 | |
|             PlayRangeGUI(TimelineItemArea.Header);
 | |
|             TimeCursorGUI(TimelineItemArea.Header);
 | |
| 
 | |
|             SplitterGUI();
 | |
|         }
 | |
| 
 | |
|         void DrawToolbar()
 | |
|         {
 | |
|             DrawOptions();
 | |
|             using (new GUILayout.VerticalScope())
 | |
|             {
 | |
|                 using (new GUILayout.HorizontalScope(EditorStyles.toolbar))
 | |
|                 {
 | |
|                     using (new GUILayout.HorizontalScope())
 | |
|                     {
 | |
|                         DrawTransportToolbar();
 | |
|                     }
 | |
| 
 | |
|                     DrawSequenceSelector();
 | |
|                     DrawBreadcrumbs();
 | |
|                     GUILayout.FlexibleSpace();
 | |
|                     DrawOptions();
 | |
|                 }
 | |
| 
 | |
|                 using (new GUILayout.HorizontalScope())
 | |
|                 {
 | |
|                     DrawHeaderEditButtons();
 | |
|                     DrawTimelineRuler();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void SubTimelineRangeGUI()
 | |
|         {
 | |
|             if (!state.IsEditingASubTimeline() || state.IsEditingAnEmptyTimeline()) return;
 | |
| 
 | |
|             var subTimelineOverlayColor = DirectorStyles.Instance.customSkin.colorSubSequenceOverlay;
 | |
| 
 | |
|             var range = state.editSequence.GetEvaluableRange();
 | |
|             var area = new Vector2(state.TimeToPixel(range.start), state.TimeToPixel(range.end));
 | |
| 
 | |
|             var fullRect = sequenceContentRect;
 | |
|             fullRect.yMin = WindowConstants.timeAreaYPosition + 1.0f;
 | |
| 
 | |
|             if (fullRect.xMin < area.x)
 | |
|             {
 | |
|                 var before = fullRect;
 | |
|                 before.xMin = fullRect.xMin;
 | |
|                 before.xMax = Mathf.Min(area.x, fullRect.xMax);
 | |
|                 EditorGUI.DrawRect(before, subTimelineOverlayColor);
 | |
|             }
 | |
| 
 | |
|             if (fullRect.xMax > area.y)
 | |
|             {
 | |
|                 var after = fullRect;
 | |
|                 after.xMin = Mathf.Max(area.y, fullRect.xMin);
 | |
|                 after.xMax = fullRect.xMax;
 | |
|                 EditorGUI.DrawRect(after, subTimelineOverlayColor);
 | |
| 
 | |
|                 // Space above the vertical scrollbar
 | |
|                 after.xMin = after.xMax;
 | |
|                 after.width = verticalScrollbarWidth;
 | |
|                 after.yMax = state.timeAreaRect.y + state.timeAreaRect.height + (state.showMarkerHeader ? WindowConstants.markerRowHeight : 0.0f);
 | |
|                 EditorGUI.DrawRect(after, subTimelineOverlayColor);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void DrawOptions()
 | |
|         {
 | |
|             if (currentMode.headerState.options == TimelineModeGUIState.Hidden || state.editSequence.asset == null)
 | |
|                 return;
 | |
| 
 | |
|             using (new EditorGUI.DisabledScope(currentMode.headerState.options == TimelineModeGUIState.Disabled))
 | |
|             {
 | |
|                 var rect = new Rect(position.width - WindowConstants.cogButtonWidth, 0, WindowConstants.cogButtonWidth, WindowConstants.timeAreaYPosition);
 | |
|                 if (EditorGUI.DropdownButton(rect, DirectorStyles.optionsCogIcon, FocusType.Keyboard, EditorStyles.toolbarButton))
 | |
|                 {
 | |
|                     GenericMenu menu = new GenericMenu();
 | |
| 
 | |
|                     menu.AddItem(L10n.TextContent("Preferences Page..."), false, () => SettingsWindow.Show(SettingsScope.User, "Preferences/Timeline"));
 | |
|                     menu.AddSeparator("");
 | |
| 
 | |
|                     menu.AddItem(MenuItemFrames, state.timeFormat == TimeFormat.Frames, () => state.timeFormat = TimeFormat.Frames);
 | |
|                     menu.AddItem(MenuItemTimecode, state.timeFormat == TimeFormat.Timecode, () => state.timeFormat = TimeFormat.Timecode);
 | |
|                     menu.AddItem(MenuItemSeconds, state.timeFormat == TimeFormat.Seconds, () => state.timeFormat = TimeFormat.Seconds);
 | |
| 
 | |
|                     menu.AddSeparator("");
 | |
| 
 | |
|                     TimeAreaContextMenu.AddTimeAreaMenuItems(menu, state);
 | |
| 
 | |
|                     menu.AddSeparator("");
 | |
|                     bool isCustom = !FrameRateDisplayUtility.GetStandardFromFrameRate(state.editSequence.frameRate, out StandardFrameRates option);
 | |
|                     var frameRatesLabels = FrameRateDisplayUtility.GetDefaultFrameRatesLabels(option);
 | |
|                     if (isCustom)
 | |
|                         frameRatesLabels[frameRatesLabels.Length - 1] = String.Format(k_CustomFpsLabel, frameRatesLabels.Last(), state.editSequence.frameRate);
 | |
| 
 | |
|                     for (var i = 0; i < frameRatesLabels.Length; i++)
 | |
|                     {
 | |
|                         var currentStandard = (StandardFrameRates)i;
 | |
|                         AddStandardFrameRateMenu(menu, currentStandard, frameRatesLabels[i], currentStandard == option);
 | |
|                     }
 | |
| 
 | |
|                     if (Unsupported.IsDeveloperMode())
 | |
|                     {
 | |
|                         menu.AddSeparator("");
 | |
|                         menu.AddItem(L10n.TextContent("Show Snapping Debug"), SnapEngine.displayDebugLayout,
 | |
|                             () => SnapEngine.displayDebugLayout = !SnapEngine.displayDebugLayout);
 | |
| 
 | |
|                         menu.AddItem(L10n.TextContent("Debug TimeArea"), false,
 | |
|                             () =>
 | |
|                                 Debug.LogFormat("translation: {0}   scale: {1}   rect: {2}   shownRange: {3}", m_TimeArea.translation, m_TimeArea.scale, m_TimeArea.rect, m_TimeArea.shownArea));
 | |
| 
 | |
|                         menu.AddItem(L10n.TextContent("Edit Skin"), false, () => Selection.activeObject = DirectorStyles.Instance.customSkin);
 | |
| 
 | |
|                         menu.AddItem(L10n.TextContent("Show QuadTree Debugger"), state.showQuadTree,
 | |
|                             () => state.showQuadTree = !state.showQuadTree);
 | |
|                     }
 | |
| 
 | |
|                     menu.DropDown(rect);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void AddStandardFrameRateMenu(GenericMenu menu, StandardFrameRates option, string label, bool on)
 | |
|         {
 | |
|             var gui = EditorGUIUtility.TextContent(String.Format(k_FrameRateMenuLabel, label));
 | |
|             if (state.editSequence.isReadOnly)
 | |
|             {
 | |
|                 menu.AddDisabledItem(gui, on);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             FrameRate value = TimeUtility.ToFrameRate(option);
 | |
|             if (!value.IsValid())
 | |
|                 menu.AddItem(gui, true, () => { });
 | |
|             else
 | |
|             {
 | |
|                 menu.AddItem(gui, on, r =>
 | |
|                 {
 | |
|                     state.editSequence.frameRate = (float)value.rate;
 | |
|                 }, value);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void AddUserOverlay(IMarker marker, Rect rect, MarkerEditor editor, bool collapsed, bool selected)
 | |
|         {
 | |
|             if (marker == null)
 | |
|                 throw new ArgumentNullException("marker");
 | |
|             if (editor == null)
 | |
|                 throw new ArgumentNullException("editor");
 | |
| 
 | |
|             m_OverlayQueue.Add(new MarkerOverlay()
 | |
|             {
 | |
|                 isCollapsed = collapsed,
 | |
|                 isSelected = selected,
 | |
|                 marker = marker,
 | |
|                 rect = rect,
 | |
|                 editor = editor
 | |
|             }
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| }
 |