Initial commit

This commit is contained in:
AbstractConcept 2022-09-13 00:36:34 -05:00
commit 3c7cc0c973
8391 changed files with 704313 additions and 0 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 37472f5179ca2004489ac901814cdbc3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
namespace UnityEditor.Timeline
{
enum TimeReferenceMode
{
Local = 0,
Global = 1
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 34d6f60b171c1004e8335d52c65928a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,42 @@
namespace UnityEditor.Timeline
{
class TimelineActiveMode : TimelineMode
{
public TimelineActiveMode()
{
headerState = new HeaderState
{
breadCrumb = TimelineModeGUIState.Enabled,
options = TimelineModeGUIState.Enabled,
sequenceSelector = TimelineModeGUIState.Enabled
};
trackOptionsState = new TrackOptionsState
{
newButton = TimelineModeGUIState.Enabled,
editAsAssetButton = TimelineModeGUIState.Hidden
};
mode = TimelineModes.Active;
}
public override bool ShouldShowTimeCursor(WindowState state)
{
return true;
}
public override bool ShouldShowPlayRange(WindowState state)
{
return state.playRangeEnabled;
}
public override TimelineModeGUIState ToolbarState(WindowState state)
{
return TimelineModeGUIState.Enabled;
}
public override TimelineModeGUIState TrackState(WindowState state)
{
return TimelineModeGUIState.Enabled;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67ee43b2f6148de40861b289b0e00591
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
namespace UnityEditor.Timeline
{
class TimelineAssetEditionMode : TimelineInactiveMode
{
public override TimelineModeGUIState TrackState(WindowState state)
{
return TimelineModeGUIState.Enabled;
}
public TimelineAssetEditionMode()
{
headerState = new HeaderState
{
breadCrumb = TimelineModeGUIState.Enabled,
options = TimelineModeGUIState.Enabled,
sequenceSelector = TimelineModeGUIState.Enabled
};
trackOptionsState = new TrackOptionsState
{
newButton = TimelineModeGUIState.Enabled,
editAsAssetButton = TimelineModeGUIState.Enabled
};
mode = TimelineModes.AssetEdition;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3477d28057cb3e4469c7ea6b8dc23046
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,44 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
class TimelineDisabledMode : TimelineMode
{
public TimelineDisabledMode()
{
headerState = new HeaderState
{
breadCrumb = TimelineModeGUIState.Enabled,
options = TimelineModeGUIState.Enabled,
sequenceSelector = TimelineModeGUIState.Enabled
};
trackOptionsState = new TrackOptionsState
{
newButton = TimelineModeGUIState.Enabled,
editAsAssetButton = TimelineModeGUIState.Enabled
};
mode = TimelineModes.Disabled;
}
public override bool ShouldShowPlayRange(WindowState state)
{
return false;
}
public override bool ShouldShowTimeCursor(WindowState state)
{
return true;
}
public override TimelineModeGUIState ToolbarState(WindowState state)
{
return TimelineModeGUIState.Disabled;
}
public override TimelineModeGUIState TrackState(WindowState state)
{
return TimelineModeGUIState.Enabled;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c5eb52d37bb6714a98af73df7d9cf2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,47 @@
namespace UnityEditor.Timeline
{
class TimelineInactiveMode : TimelineMode
{
public TimelineInactiveMode()
{
headerState = new HeaderState
{
breadCrumb = TimelineModeGUIState.Disabled,
options = TimelineModeGUIState.Enabled,
sequenceSelector = TimelineModeGUIState.Enabled
};
trackOptionsState = new TrackOptionsState
{
newButton = TimelineModeGUIState.Disabled,
editAsAssetButton = TimelineModeGUIState.Enabled
};
mode = TimelineModes.Inactive;
}
public override bool ShouldShowPlayRange(WindowState state)
{
return false;
}
public override bool ShouldShowTimeCursor(WindowState state)
{
return false;
}
public override TimelineModeGUIState ToolbarState(WindowState state)
{
return TimelineModeGUIState.Disabled;
}
public override TimelineModeGUIState TrackState(WindowState state)
{
return TimelineModeGUIState.Disabled;
}
public override TimelineModeGUIState PreviewState(WindowState state)
{
return TimelineModeGUIState.Disabled;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5503f95d174761548a68a901beab13c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,71 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
enum TimelineModeGUIState
{
Disabled,
Hidden,
Enabled
}
abstract class TimelineMode
{
public struct HeaderState
{
public TimelineModeGUIState breadCrumb;
public TimelineModeGUIState sequenceSelector;
public TimelineModeGUIState options;
}
public struct TrackOptionsState
{
public TimelineModeGUIState newButton;
public TimelineModeGUIState editAsAssetButton;
}
public HeaderState headerState { get; protected set; }
public TrackOptionsState trackOptionsState { get; protected set; }
public TimelineModes mode { get; protected set; }
public abstract bool ShouldShowPlayRange(WindowState state);
public abstract bool ShouldShowTimeCursor(WindowState state);
public virtual bool ShouldShowTrackBindings(WindowState state)
{
return ShouldShowTimeCursor(state);
}
public virtual bool ShouldShowTimeArea(WindowState state)
{
return !state.IsEditingAnEmptyTimeline();
}
public abstract TimelineModeGUIState TrackState(WindowState state);
public abstract TimelineModeGUIState ToolbarState(WindowState state);
public virtual TimelineModeGUIState PreviewState(WindowState state)
{
return Application.isPlaying ? TimelineModeGUIState.Disabled : TimelineModeGUIState.Enabled;
}
public virtual TimelineModeGUIState EditModeButtonsState(WindowState state)
{
return TimelineModeGUIState.Enabled;
}
}
[Flags]
internal enum TimelineModes
{
None = 0,
Active = 1,
ReadOnly = 2,
Inactive = 4,
Disabled = 8,
AssetEdition = 16,
All = Active | ReadOnly | Inactive | Disabled,
Default = Active | AssetEdition
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2cb43d6b0c226443be7e176590837a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,52 @@
namespace UnityEditor.Timeline
{
class TimelineReadOnlyMode : TimelineMode
{
public TimelineReadOnlyMode()
{
headerState = new HeaderState()
{
breadCrumb = TimelineModeGUIState.Enabled,
options = TimelineModeGUIState.Enabled,
sequenceSelector = TimelineModeGUIState.Enabled,
};
trackOptionsState = new TrackOptionsState()
{
newButton = TimelineModeGUIState.Disabled,
editAsAssetButton = TimelineModeGUIState.Disabled,
};
mode = TimelineModes.ReadOnly;
}
public override bool ShouldShowPlayRange(WindowState state)
{
return state.editSequence.director != null && state.playRangeEnabled;
}
public override bool ShouldShowTimeCursor(WindowState state)
{
return state.editSequence.director != null;
}
public override TimelineModeGUIState TrackState(WindowState state)
{
return TimelineModeGUIState.Disabled;
}
public override TimelineModeGUIState ToolbarState(WindowState state)
{
return state.editSequence.director == null ? TimelineModeGUIState.Disabled : TimelineModeGUIState.Enabled;
}
public override TimelineModeGUIState PreviewState(WindowState state)
{
return state.editSequence.director == null ? TimelineModeGUIState.Disabled : TimelineModeGUIState.Enabled;
}
public override TimelineModeGUIState EditModeButtonsState(WindowState state)
{
return TimelineModeGUIState.Disabled;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f8643c1f8dd449e85b548a14edbea2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,54 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
enum PlaybackScrollMode
{
None,
Pan,
Smooth
}
static class PlaybackScroller
{
public static void AutoScroll(WindowState state)
{
if (Event.current.type != EventType.Layout)
return;
switch (state.autoScrollMode)
{
case PlaybackScrollMode.Pan:
DoPanScroll(state);
break;
case PlaybackScrollMode.Smooth:
DoSmoothScroll(state);
break;
}
}
static void DoSmoothScroll(WindowState state)
{
if (state.playing)
state.SetPlayHeadToMiddle();
state.UpdateLastFrameTime();
}
static void DoPanScroll(WindowState state)
{
if (!state.playing)
return;
var paddingDeltaTime = state.PixelDeltaToDeltaTime(WindowConstants.autoPanPaddingInPixels);
var showRange = state.timeAreaShownRange;
var rightBoundForPan = showRange.y - paddingDeltaTime;
if (state.editSequence.time > rightBoundForPan)
{
var leftBoundForPan = showRange.x + paddingDeltaTime;
var delta = rightBoundForPan - leftBoundForPan;
state.SetTimeAreaShownRange(showRange.x + delta, showRange.y + delta);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98545765d7a2b614b921715928035ee2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,201 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class TimelineMarkerHeaderGUI : IRowGUI, ILayerable
{
int m_TrackHash;
TimelineAsset timeline { get; }
WindowState state { get; }
MarkersLayer m_Layer;
LayerZOrder m_ZOrder = new LayerZOrder(Layer.MarkerHeaderTrack, 0);
struct DrawData
{
public Rect headerRect;
public Rect contentRect;
public GUIStyle trackSwatchStyle;
public GUIStyle trackHeaderFont;
public Color colorTrackFont;
public bool showLockButton;
public bool showMuteButton;
}
public TimelineMarkerHeaderGUI(TimelineAsset asset, WindowState state)
{
m_TrackHash = -1;
timeline = asset;
this.state = state;
}
public TrackAsset asset { get { return timeline.markerTrack; } }
public Rect boundingRect { get; private set; }
public bool locked { get { return !state.showMarkerHeader; } }
public bool showMarkers
{
get { return state.showMarkerHeader; }
}
public bool muted
{
get { return timeline.markerTrack != null && timeline.markerTrack.muted; }
}
Rect IRowGUI.ToWindowSpace(Rect rect)
{
//header gui is already in global coordinates
return rect;
}
public void Draw(Rect markerHeaderRect, Rect markerContentRect, WindowState state)
{
boundingRect = markerContentRect;
var data = new DrawData()
{
headerRect = markerHeaderRect,
contentRect = markerContentRect,
trackSwatchStyle = new GUIStyle(),
trackHeaderFont = DirectorStyles.Instance.trackHeaderFont,
colorTrackFont = DirectorStyles.Instance.customSkin.colorTrackFont,
showLockButton = locked,
showMuteButton = muted
};
if (state.showMarkerHeader)
{
DrawMarkerDrawer(data, state);
if (Event.current.type == EventType.Repaint)
state.spacePartitioner.AddBounds(this, boundingRect);
}
if (asset != null && Hash() != m_TrackHash)
Rebuild();
var rect = state.showMarkerHeader ? markerContentRect : state.timeAreaRect;
using (new GUIViewportScope(rect))
{
if (m_Layer != null)
m_Layer.Draw(rect, state);
HandleDragAndDrop();
}
}
public void Rebuild()
{
if (asset == null)
return;
m_Layer = new MarkersLayer(Layer.MarkersOnHeader, this);
m_TrackHash = Hash();
}
void HandleDragAndDrop()
{
if (TimelineWindow.instance.state.editSequence.isReadOnly)
return;
if (Event.current == null || Event.current.type != EventType.DragUpdated &&
Event.current.type != EventType.DragPerform && Event.current.type != EventType.DragExited)
return;
timeline.CreateMarkerTrack(); // Ensure Marker track is created.
var objectsBeingDropped = DragAndDrop.objectReferences.OfType<Object>();
var candidateTime = TimelineHelpers.GetCandidateTime(TimelineWindow.instance.state, Event.current.mousePosition);
var perform = Event.current.type == EventType.DragPerform;
var director = state.editSequence != null ? state.editSequence.director : null;
DragAndDrop.visualMode = TimelineDragging.HandleClipPaneObjectDragAndDrop(objectsBeingDropped, timeline.markerTrack, perform,
timeline, null, director, candidateTime, TimelineDragging.ResolveType);
if (perform && DragAndDrop.visualMode == DragAndDropVisualMode.Copy)
{
DragAndDrop.AcceptDrag();
}
}
int Hash()
{
return timeline.markerTrack == null ? 0 : timeline.markerTrack.Hash();
}
static void DrawMarkerDrawer(DrawData data, WindowState state)
{
DrawMarkerDrawerHeaderBackground(data);
DrawMarkerDrawerHeader(data, state);
DrawMarkerDrawerContentBackground(data);
}
static void DrawMarkerDrawerHeaderBackground(DrawData data)
{
var backgroundColor = DirectorStyles.Instance.customSkin.markerHeaderDrawerBackgroundColor;
var bgRect = data.headerRect;
bgRect.x += data.trackSwatchStyle.fixedWidth;
bgRect.width -= data.trackSwatchStyle.fixedWidth;
EditorGUI.DrawRect(bgRect, backgroundColor);
}
static void DrawMarkerDrawerHeader(DrawData data, WindowState state)
{
var textStyle = data.trackHeaderFont;
textStyle.normal.textColor = data.colorTrackFont;
var labelRect = data.headerRect;
labelRect.x += DirectorStyles.kBaseIndent;
EditorGUI.LabelField(labelRect, DirectorStyles.timelineMarkerTrackHeader);
const float buttonSize = WindowConstants.trackHeaderButtonSize;
const float padding = WindowConstants.trackHeaderButtonPadding;
var x = data.headerRect.xMax - buttonSize - padding - 2f;
var y = data.headerRect.y + (data.headerRect.height - buttonSize) / 2.0f;
var buttonRect = new Rect(x, y, buttonSize, buttonSize);
DrawTrackDropDownMenu(buttonRect, state);
buttonRect.x -= 16.0f;
if (data.showMuteButton)
{
DrawMuteButton(buttonRect, state);
buttonRect.x -= 16.0f;
}
if (data.showLockButton)
{
DrawLockButton(buttonRect, state);
}
}
static void DrawMarkerDrawerContentBackground(DrawData data)
{
var trackBackgroundColor = DirectorStyles.Instance.customSkin.markerDrawerBackgroundColor;
EditorGUI.DrawRect(data.contentRect, trackBackgroundColor);
}
static void DrawLockButton(Rect rect, WindowState state)
{
if (GUI.Button(rect, GUIContent.none, TimelineWindow.styles.locked))
TimelineAction.Invoke<ToggleShowMarkersOnTimeline>(state);
}
static void DrawTrackDropDownMenu(Rect rect, WindowState state)
{
rect.y += WindowConstants.trackOptionButtonVerticalPadding;
if (GUI.Button(rect, GUIContent.none, DirectorStyles.Instance.trackOptions))
SequencerContextMenu.ShowMarkerHeaderContextMenu(null, state);
}
static void DrawMuteButton(Rect rect, WindowState state)
{
if (GUI.Button(rect, GUIContent.none, TimelineWindow.styles.mute))
TimelineAction.Invoke<ToggleMuteMarkersOnTimeline>(state);
}
public LayerZOrder zOrder
{
get { return m_ZOrder; }
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4068e97704a16794ea218ba560cdc1e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,541 @@
using System;
using System.Collections.Generic;
using UnityEditor.Callbacks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
[EditorWindowTitle(title = "Timeline", useTypeNameAsIconName = true)]
partial class TimelineWindow : EditorWindow, IHasCustomMenu
{
[Serializable]
public class TimelineWindowPreferences
{
public bool frameSnap = true;
public bool edgeSnaps = true;
public bool muteAudioScrub = true;
public bool playRangeLoopMode = true;
public PlaybackScrollMode autoScrollMode;
public EditMode.EditType editType = EditMode.EditType.Mix;
public TimeReferenceMode timeReferenceMode = TimeReferenceMode.Local;
}
[SerializeField] TimelineWindowPreferences m_Preferences = new TimelineWindowPreferences();
public TimelineWindowPreferences preferences { get { return m_Preferences; } }
[SerializeField]
EditorGUIUtility.EditorLockTracker m_LockTracker = new EditorGUIUtility.EditorLockTracker();
readonly PreviewResizer m_PreviewResizer = new PreviewResizer();
bool m_LastFrameHadSequence;
bool m_ForceRefreshLastSelection;
int m_CurrentSceneHashCode = -1;
[NonSerialized]
bool m_HasBeenInitialized;
[SerializeField]
SequenceHierarchy m_SequenceHierarchy;
static SequenceHierarchy s_LastHierarchy;
public static TimelineWindow instance { get; private set; }
public Rect clientArea { get; set; }
public bool isDragging { get; set; }
public static DirectorStyles styles { get { return DirectorStyles.Instance; } }
public List<TimelineTrackBaseGUI> allTracks
{
get
{
return treeView != null ? treeView.allTrackGuis : new List<TimelineTrackBaseGUI>();
}
}
public WindowState state { get; private set; }
public bool locked
{
get
{
// we can never be in a locked state if there is no timeline asset
if (state.editSequence.asset == null)
return false;
return m_LockTracker.isLocked;
}
set { m_LockTracker.isLocked = value; }
}
public bool hierarchyChangedThisFrame { get; private set; }
public TimelineWindow()
{
InitializeManipulators();
m_LockTracker.lockStateChanged.AddPersistentListener(OnLockStateChanged, UnityEventCallState.EditorAndRuntime);
}
void OnLockStateChanged(bool locked)
{
// Make sure that upon unlocking, any selection change is updated
// Case 1123119 -- only force rebuild if not recording
if (!locked)
RefreshSelection(state != null && !state.recording);
}
void OnEnable()
{
if (m_SequencePath == null)
m_SequencePath = new SequencePath();
if (m_SequenceHierarchy == null)
{
// The sequence hierarchy will become null if maximize on play is used for in/out of playmode
// a static var will hang on to the reference
if (s_LastHierarchy != null)
m_SequenceHierarchy = s_LastHierarchy;
else
m_SequenceHierarchy = SequenceHierarchy.CreateInstance();
state = null;
}
s_LastHierarchy = m_SequenceHierarchy;
titleContent = GetLocalizedTitleContent();
m_PreviewResizer.Init("TimelineWindow");
// Unmaximize fix : when unmaximizing, a new window is enabled and disabled. Prevent it from overriding the instance pointer.
if (instance == null)
instance = this;
AnimationClipCurveCache.Instance.OnEnable();
TrackAsset.OnClipPlayableCreate += m_PlayableLookup.UpdatePlayableLookup;
TrackAsset.OnTrackAnimationPlayableCreate += m_PlayableLookup.UpdatePlayableLookup;
if (state == null)
{
state = new WindowState(this, s_LastHierarchy);
Initialize();
RefreshSelection(true);
m_ForceRefreshLastSelection = true;
}
}
void OnDisable()
{
if (instance == this)
instance = null;
if (state != null)
state.Reset();
if (instance == null)
SelectionManager.RemoveTimelineSelection();
AnimationClipCurveCache.Instance.OnDisable();
TrackAsset.OnClipPlayableCreate -= m_PlayableLookup.UpdatePlayableLookup;
TrackAsset.OnTrackAnimationPlayableCreate -= m_PlayableLookup.UpdatePlayableLookup;
TimelineWindowViewPrefs.SaveAll();
TimelineWindowViewPrefs.UnloadAllViewModels();
}
void OnDestroy()
{
if (state != null)
{
state.OnDestroy();
}
m_HasBeenInitialized = false;
RemoveEditorCallbacks();
AnimationClipCurveCache.Instance.Clear();
TimelineAnimationUtilities.UnlinkAnimationWindow();
}
void OnLostFocus()
{
isDragging = false;
if (state != null)
state.captured.Clear();
Repaint();
}
void OnFocus()
{
if (state == null) return;
if (lastSelectedGO != Selection.activeObject)
{
// selection may have changed while Timeline Editor was looking away
RefreshSelection(false);
// Inline curves may have become out of sync
RefreshInlineCurves();
}
}
void OnHierarchyChange()
{
hierarchyChangedThisFrame = true;
Repaint();
}
void OnStateChange()
{
state.UpdateRecordingState();
if (treeView != null && state.editSequence.asset != null)
treeView.Reload();
if (m_MarkerHeaderGUI != null)
m_MarkerHeaderGUI.Rebuild();
}
void OnGUI()
{
InitializeGUIIfRequired();
UpdateGUIConstants();
UpdateViewStateHash();
EditMode.HandleModeClutch(); // TODO We Want that here?
DetectStylesChange();
DetectActiveSceneChanges();
DetectStateChanges();
state.ProcessStartFramePendingUpdates();
var clipRect = new Rect(0.0f, 0.0f, position.width, position.height);
clipRect.xMin += state.sequencerHeaderWidth;
using (new GUIViewportScope(clipRect))
state.InvokeWindowOnGuiStarted(Event.current);
if (Event.current.type == EventType.MouseDrag && state != null && state.mouseDragLag > 0.0f)
{
state.mouseDragLag -= Time.deltaTime;
return;
}
if (PerformUndo())
return;
if (EditorApplication.isPlaying)
{
if (state != null)
{
if (state.recording)
state.recording = false;
}
Repaint();
}
clientArea = position;
PlaybackScroller.AutoScroll(state);
DoLayout();
// overlays
if (state.captured.Count > 0)
{
using (new GUIViewportScope(clipRect))
{
foreach (var o in state.captured)
{
o.Overlay(Event.current, state);
}
Repaint();
}
}
if (state.showQuadTree)
state.spacePartitioner.DebugDraw();
// attempt another rebuild -- this will avoid 1 frame flashes
if (Event.current.type == EventType.Repaint)
{
RebuildGraphIfNecessary();
state.ProcessEndFramePendingUpdates();
}
using (new GUIViewportScope(clipRect))
{
if (Event.current.type == EventType.Repaint)
EditMode.inputHandler.OnGUI(state, Event.current);
}
if (Event.current.type == EventType.Repaint)
hierarchyChangedThisFrame = false;
}
static void DetectStylesChange()
{
DirectorStyles.ReloadStylesIfNeeded();
}
void DetectActiveSceneChanges()
{
if (m_CurrentSceneHashCode == -1)
{
m_CurrentSceneHashCode = SceneManager.GetActiveScene().GetHashCode();
}
if (m_CurrentSceneHashCode != SceneManager.GetActiveScene().GetHashCode())
{
bool isSceneStillLoaded = false;
for (int a = 0; a < SceneManager.sceneCount; a++)
{
var scene = SceneManager.GetSceneAt(a);
if (scene.GetHashCode() == m_CurrentSceneHashCode && scene.isLoaded)
{
isSceneStillLoaded = true;
break;
}
}
if (!isSceneStillLoaded)
{
if (!locked)
ClearCurrentTimeline();
m_CurrentSceneHashCode = SceneManager.GetActiveScene().GetHashCode();
}
}
}
void DetectStateChanges()
{
if (state != null)
{
state.editSequence.ResetIsReadOnly(); //Force reset readonly for asset flag for each frame.
// detect if the sequence was removed under our feet
if (m_LastFrameHadSequence && state.editSequence.asset == null)
{
ClearCurrentTimeline();
}
m_LastFrameHadSequence = state.editSequence.asset != null;
// the currentDirector can get set to null by a deletion or scene unloading so polling is required
if (state.editSequence.director == null)
{
state.recording = false;
state.previewMode = false;
//Case 1201405 : Check if the lock state is valid with the lock tracker state
if (locked != m_LockTracker.isLocked)
m_LockTracker.isLocked = locked;
if (!locked && m_LastFrameHadSequence)
{
// the user may be adding a new PlayableDirector to a selected GameObject, make sure the timeline editor is shows the proper director if none is already showing
var selectedGameObject = Selection.activeObject != null ? Selection.activeObject as GameObject : null;
var selectedDirector = selectedGameObject != null ? selectedGameObject.GetComponent<PlayableDirector>() : null;
if (selectedDirector != null)
{
SetCurrentTimeline(selectedDirector);
}
}
}
else
{
// the user may have changed the timeline associated with the current director
if (state.editSequence.asset != state.editSequence.director.playableAsset)
{
if (!locked)
{
SetCurrentTimeline(state.editSequence.director);
}
else
{
// Keep locked on the current timeline but set the current director to null since it's not the timeline owner anymore
SetCurrentTimeline(state.editSequence.asset);
}
}
}
}
}
void Initialize()
{
if (!m_HasBeenInitialized)
{
InitializeStateChange();
InitializeEditorCallbacks();
m_HasBeenInitialized = true;
}
}
void RefreshLastSelectionIfRequired()
{
// case 1088918 - workaround for the instanceID to object cache being update during Awake.
// This corrects any playableDirector ptrs with the correct cached version
// This can happen when going from edit to playmode
if (m_ForceRefreshLastSelection)
{
m_ForceRefreshLastSelection = false;
RestoreLastSelection(true);
}
}
void InitializeGUIIfRequired()
{
RefreshLastSelectionIfRequired();
InitializeTimeArea();
if (treeView == null && state.editSequence.asset != null)
{
treeView = new TimelineTreeViewGUI(this, state.editSequence.asset, position);
}
}
void UpdateGUIConstants()
{
m_HorizontalScrollBarSize =
GUI.skin.horizontalScrollbar.fixedHeight + GUI.skin.horizontalScrollbar.margin.top;
m_VerticalScrollBarSize = (treeView != null && treeView.showingVerticalScrollBar)
? GUI.skin.verticalScrollbar.fixedWidth + GUI.skin.verticalScrollbar.margin.left
: 0;
}
void UpdateViewStateHash()
{
if (Event.current.type == EventType.Layout)
state.UpdateViewStateHash();
}
static bool PerformUndo()
{
if (!Event.current.isKey)
return false;
if (Event.current.keyCode != KeyCode.Z)
return false;
if (!EditorGUI.actionKey)
return false;
return true;
}
public void RebuildGraphIfNecessary(bool evaluate = true)
{
if (state == null || state.editSequence.director == null || state.editSequence.asset == null)
return;
if (state.rebuildGraph)
{
// rebuilding the graph resets the time
double time = state.editSequence.time;
var wasPlaying = false;
// disable preview mode,
if (!EditorApplication.isPlaying)
{
wasPlaying = state.playing;
state.previewMode = false;
state.GatherProperties(state.masterSequence.director);
}
state.RebuildPlayableGraph();
state.editSequence.time = time;
if (wasPlaying)
state.Play();
if (evaluate)
{
// put the scene back in the correct state
state.EvaluateImmediate();
// this is necessary to see accurate results when inspector refreshes
// case 1154802 - this will property re-force time on the director, so
// the play head won't snap back to the timeline duration on rebuilds
if (!state.playing)
state.Evaluate();
}
Repaint();
}
state.rebuildGraph = false;
}
// for tests
public new void RepaintImmediately()
{
base.RepaintImmediately();
}
internal static bool IsEditingTimelineAsset(TimelineAsset timelineAsset)
{
return instance != null && instance.state != null && instance.state.editSequence.asset == timelineAsset;
}
internal static void RepaintIfEditingTimelineAsset(TimelineAsset timelineAsset)
{
if (IsEditingTimelineAsset(timelineAsset))
instance.Repaint();
}
internal class DoCreateTimeline : ProjectWindowCallback.EndNameEditAction
{
public override void Action(int instanceId, string pathName, string resourceFile)
{
var timeline = ScriptableObject.CreateInstance<TimelineAsset>();
AssetDatabase.CreateAsset(timeline, pathName);
ProjectWindowUtil.ShowCreatedAsset(timeline);
}
}
[MenuItem("Assets/Create/Timeline", false, 450)]
public static void CreateNewTimeline()
{
var icon = EditorGUIUtility.IconContent("TimelineAsset Icon").image as Texture2D;
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance<DoCreateTimeline>(), "New Timeline.playable", icon, null);
}
[MenuItem("Window/Sequencing/Timeline", false, 1)]
public static void ShowWindow()
{
GetWindow<TimelineWindow>(typeof(SceneView));
instance.Focus();
}
[OnOpenAsset(1)]
public static bool OnDoubleClick(int instanceID, int line)
{
var assetDoubleClicked = EditorUtility.InstanceIDToObject(instanceID) as TimelineAsset;
if (assetDoubleClicked == null)
return false;
ShowWindow();
instance.SetCurrentTimeline(assetDoubleClicked);
return true;
}
public virtual void AddItemsToMenu(GenericMenu menu)
{
bool disabled = state == null || state.editSequence.asset == null;
m_LockTracker.AddItemsToMenu(menu, disabled);
}
protected virtual void ShowButton(Rect r)
{
bool disabled = state == null || state.editSequence.asset == null;
m_LockTracker.ShowButton(r, DirectorStyles.Instance.lockButton, disabled);
}
internal void TreeViewKeyboardCallback()
{
if (Event.current.type != EventType.KeyDown)
return;
if (TimelineAction.HandleShortcut(state, Event.current))
{
Event.current.Use();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f817a38900380be47942905e17e7d39b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,331 @@
using System;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class TimelineWindowTimeControl : IAnimationWindowControl
{
[Serializable]
public struct ClipData
{
public double start;
public double duration;
public TrackAsset track;
}
[SerializeField] ClipData m_ClipData;
[SerializeField] TimelineClip m_Clip;
[SerializeField] AnimationWindowState m_AnimWindowState;
TrackAsset track
{
get
{
if (m_Clip != null)
{
return m_Clip.parentTrack;
}
return m_ClipData.track;
}
}
static TimelineWindow window
{
get
{
return TimelineWindow.instance;
}
}
static WindowState state
{
get
{
if (window != null)
return window.state;
return null;
}
}
void OnStateChange()
{
if (state != null && state.dirtyStamp > 0 && m_AnimWindowState != null)
m_AnimWindowState.Repaint();
}
public void Init(AnimationWindowState animState, TimelineClip clip)
{
m_Clip = clip;
m_AnimWindowState = animState;
}
public void Init(AnimationWindowState animState, ClipData clip)
{
m_ClipData = clip;
m_AnimWindowState = animState;
}
public override void OnEnable()
{
if (state != null)
state.OnTimeChange += OnStateChange;
base.OnEnable();
}
public void OnDisable()
{
if (state != null)
state.OnTimeChange -= OnStateChange;
}
public override AnimationKeyTime time
{
get
{
if (state == null)
return AnimationKeyTime.Time(0.0f, 0.0f);
return AnimationKeyTime.Time(ToAnimationClipTime(state.editSequence.time), state.referenceSequence.frameRate);
}
}
void ChangeTime(float newTime)
{
if (state != null && state.editSequence.director != null)
{
// avoid rounding errors
var finalTime = ToGlobalTime(newTime);
if (TimeUtility.OnFrameBoundary(finalTime, state.referenceSequence.frameRate, TimeUtility.kFrameRateEpsilon))
finalTime = TimeUtility.RoundToFrame(finalTime, state.referenceSequence.frameRate);
state.editSequence.time = finalTime;
window.Repaint();
}
}
static void ChangeFrame(int frame)
{
if (state != null)
{
state.editSequence.frame = frame;
window.Repaint();
}
}
public override void GoToTime(float newTime)
{
ChangeTime(newTime);
}
public override void GoToFrame(int frame)
{
ChangeFrame(frame);
}
public override void StartScrubTime() {}
public override void EndScrubTime() {}
public override void ScrubTime(float newTime)
{
ChangeTime(newTime);
}
public override void GoToPreviousFrame()
{
if (state != null)
ChangeFrame(state.editSequence.frame - 1);
}
public override void GoToNextFrame()
{
if (state != null)
ChangeFrame(state.editSequence.frame + 1);
}
AnimationWindowCurve[] GetCurves()
{
var curves =
(m_AnimWindowState.showCurveEditor &&
m_AnimWindowState.activeCurves.Count > 0) ? m_AnimWindowState.activeCurves : m_AnimWindowState.allCurves;
return curves.ToArray();
}
public override void GoToPreviousKeyframe()
{
var newTime = AnimationWindowUtility.GetPreviousKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate);
GoToTime(m_AnimWindowState.SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToClipFrame));
}
public override void GoToNextKeyframe()
{
var newTime = AnimationWindowUtility.GetNextKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate);
GoToTime(m_AnimWindowState.SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToClipFrame));
}
public override void GoToFirstKeyframe()
{
GoToTime(0);
}
public override void GoToLastKeyframe()
{
double animClipTime = 0;
if (m_Clip != null)
{
var curves = m_Clip.curves;
var animAsset = m_Clip.asset as AnimationPlayableAsset;
if (animAsset != null)
{
animClipTime = animAsset.clip != null ? animAsset.clip.length : 0;
}
else if (curves != null)
{
animClipTime = curves.length;
}
else
{
animClipTime = m_Clip.clipAssetDuration;
}
}
else
{
animClipTime = m_ClipData.duration;
}
GoToTime((float)animClipTime);
}
public override bool canPlay
{
get
{
return state != null && state.previewMode;
}
}
public override bool playing
{
get
{
return state != null && state.playing;
}
}
static void SetPlaybackState(bool playbackState)
{
if (state == null || playbackState == state.playing)
return;
state.SetPlaying(playbackState);
}
public override bool StartPlayback()
{
SetPlaybackState(true);
return state != null && state.playing;
}
public override void StopPlayback()
{
SetPlaybackState(false);
}
public override bool PlaybackUpdate() { return state != null && state.playing; }
public override bool canRecord
{
get { return state != null && state.canRecord; }
}
public override bool recording
{
get { return state != null && state.recording; }
}
public override bool canPreview
{
get { return false; }
}
public override bool previewing
{
get { return false; }
}
public override bool StartRecording(Object targetObject)
{
if (!canRecord)
return false;
if (Application.isPlaying)
return false;
if (state != null && track != null)
{
state.ArmForRecord(track);
return state.recording;
}
return false;
}
public override void StopRecording()
{
if (Application.isPlaying)
return;
if (state != null && track != null)
state.UnarmForRecord(track);
}
public override void OnSelectionChanged() {}
public override void ResampleAnimation() {}
public override bool StartPreview()
{
if (state != null)
state.previewMode = true;
return state != null && state.previewMode;
}
public override void StopPreview()
{
if (state != null)
state.previewMode = false;
}
public override void ProcessCandidates() {}
public override void ClearCandidates() {}
double durationD
{
get
{
if (m_Clip != null)
{
return ToAnimationClipTime(m_Clip.end);
}
return m_ClipData.duration;
}
}
double ToGlobalTime(float localTime)
{
if (m_Clip != null)
return Math.Max(0, m_Clip.FromLocalTimeUnbound(localTime));
return Math.Max(0, m_ClipData.start + localTime);
}
float ToAnimationClipTime(double globalTime)
{
if (m_Clip != null)
return (float)m_Clip.ToLocalTimeUnbound(globalTime);
return (float)(globalTime - m_ClipData.start);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6f25fb081e85cb743b272c2f7fbc2f6b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,80 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
private TimelineAsset m_PreviousMasterSequence;
public void ClearCurrentTimeline()
{
SetCurrentTimeline(null, null, null, true);
}
public void SetCurrentTimeline(TimelineAsset seq)
{
SetCurrentTimeline(seq, null, null);
}
public void SetCurrentTimeline(PlayableDirector director, TimelineClip hostClip = null)
{
var asset = director != null ? director.playableAsset as TimelineAsset : null;
SetCurrentTimeline(asset, director, hostClip);
}
void SetCurrentTimeline(TimelineAsset seq, PlayableDirector instanceOfDirector, TimelineClip hostClip, bool force = false)
{
if (state == null)
return;
if (!force &&
state.editSequence.hostClip == hostClip &&
state.editSequence.director == instanceOfDirector &&
state.editSequence.asset == seq)
return;
state.SetCurrentSequence(seq, instanceOfDirector, hostClip);
}
void OnBeforeSequenceChange()
{
treeView = null;
m_MarkerHeaderGUI = null;
m_TimeAreaDirty = true;
state.Reset();
m_PlayableLookup.ClearPlayableLookup();
// clear old editors to caches, like audio previews, get flushed
CustomTimelineEditorCache.ClearCache<ClipEditor>();
CustomTimelineEditorCache.ClearCache<MarkerEditor>();
CustomTimelineEditorCache.ClearCache<TrackEditor>();
m_PreviousMasterSequence = state.masterSequence.asset;
}
void OnAfterSequenceChange()
{
Repaint();
m_SequencePath = state.GetCurrentSequencePath();
m_LastFrameHadSequence = state.editSequence.asset != null;
TimelineWindowViewPrefs.SaveAll();
// this prevent clearing the animation window when going in/out of playmode, but
// clears it when we switch master timelines
// the cast to a object will handle the case where the sequence has been deleted.
object previousMasterSequence = m_PreviousMasterSequence;
bool isDeleted = previousMasterSequence != null && m_PreviousMasterSequence == null;
bool hasChanged = m_PreviousMasterSequence != null && m_PreviousMasterSequence != state.masterSequence.asset;
if (isDeleted || hasChanged)
{
AnimationClipCurveCache.Instance.Clear();
TimelineAnimationUtilities.UnlinkAnimationWindow();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7abcd1a72bb7174ca58e813c6eee9c2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
List<BreadCrumbTitle> m_BreadCrumbLabels = new List<BreadCrumbTitle>(100);
static TitleMode GetTitleMode(ISequenceState sequence)
{
var prefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
// Top level
if (sequence.hostClip == null)
{
if (sequence.director != null && prefabStage != null && prefabStage.IsPartOfPrefabContents(sequence.director.gameObject))
return TitleMode.Prefab;
if (sequence.director != null && PrefabUtility.IsPartOfPrefabAsset(sequence.director))
return TitleMode.PrefabOutOfContext;
if (sequence.director != null && !sequence.director.isActiveAndEnabled)
return TitleMode.DisabledComponent;
if (sequence.director != null)
return TitleMode.GameObject;
if (sequence.asset != null)
return TitleMode.Asset;
}
// Subtimelines only get an error icon
else if (sequence.director != null && !sequence.director.isActiveAndEnabled && !PrefabUtility.IsPartOfPrefabAsset(sequence.director))
return TitleMode.DisabledComponent;
return TitleMode.None;
}
void DoBreadcrumbGUI()
{
if (state == null)
return;
int count = 0;
foreach (var sequence in state.GetAllSequences())
{
BreadCrumbTitle title = new BreadCrumbTitle()
{
name = DisplayNameHelper.GetDisplayName(sequence),
mode = GetTitleMode(sequence)
};
if (count >= m_BreadCrumbLabels.Count)
m_BreadCrumbLabels.Add(title);
else
m_BreadCrumbLabels[count] = title;
count++;
}
if (m_BreadCrumbLabels.Count > count)
m_BreadCrumbLabels.RemoveRange(count, m_BreadCrumbLabels.Count - count);
using (new EditorGUI.DisabledScope(currentMode.headerState.breadCrumb == TimelineModeGUIState.Disabled))
{
BreadcrumbDrawer.Draw(breadCrumbAreaWidth, m_BreadCrumbLabels, NavigateToBreadcrumbIndex);
}
}
void NavigateToBreadcrumbIndex(int index)
{
state.PopSequencesUntilCount(index + 1);
}
void DoSequenceSelectorGUI()
{
using (new EditorGUI.DisabledScope(currentMode.headerState.sequenceSelector == TimelineModeGUIState.Disabled))
{
if (EditorGUILayout.DropdownButton(DirectorStyles.timelineSelectorArrow, FocusType.Passive, DirectorStyles.Instance.sequenceSwitcher, GUILayout.Width(WindowConstants.selectorWidth)))
ShowSequenceSelector();
}
}
void ShowSequenceSelector()
{
var allDirectors = TimelineUtility.GetDirectorsInSceneUsingAsset(null);
var formatter = new SequenceMenuNameFormater();
var namesAndDirectors = new List<ValueTuple<string, PlayableDirector>>();
foreach (var d in allDirectors)
{
if (d.playableAsset is TimelineAsset)
{
var text = formatter.Format(DisplayNameHelper.GetDisplayName(d));
namesAndDirectors.Add(new ValueTuple<string, PlayableDirector>(text, d));
}
}
var sequenceMenu = new GenericMenu();
foreach (var (timelineName, playableDirector) in namesAndDirectors.OrderBy(i => i.Item1))
{
var isCurrent = state.masterSequence.director == playableDirector;
sequenceMenu.AddItem(new GUIContent(timelineName), isCurrent, OnSequenceSelected, playableDirector);
}
if (allDirectors.Length == 0)
sequenceMenu.AddDisabledItem(DirectorStyles.noTimelinesInScene);
sequenceMenu.DropDown(EditorGUILayout.s_LastRect);
}
void OnSequenceSelected(object arg)
{
var directorToBindTo = (PlayableDirector)arg;
if (directorToBindTo)
{
// don't just select the object, it may already be selected.
SetCurrentTimeline(directorToBindTo);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2cd16a2d73fe7a4c9affa2b790eb5e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,128 @@
using System;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
TimeAreaItem m_TimelineDuration;
void DurationGUI(TimelineItemArea area, double duration)
{
// don't show the duration if the time area is not visible for some other reason.
if (!currentMode.ShouldShowTimeArea(state))
return;
bool headerMode = area == TimelineItemArea.Header;
if (state.IsEditingASubTimeline())
{
if (headerMode)
HighlightTimeAreaRange(state.editSequence.GetEvaluableRange(), DirectorStyles.Instance.customSkin.colorSubSequenceDurationLine);
return;
}
// don't show the duration if there's none.
if (state.editSequence.asset.durationMode == TimelineAsset.DurationMode.BasedOnClips && duration <= 0.0f)
return;
if (m_TimelineDuration == null || m_TimelineDuration.style != styles.endmarker)
{
m_TimelineDuration = new TimeAreaItem(styles.endmarker, OnTrackDurationDrag)
{
tooltip = "End of sequence marker",
boundOffset = new Vector2(0.0f, -DirectorStyles.kDurationGuiThickness)
};
}
DrawDuration(headerMode, !headerMode, duration);
}
void DrawDuration(bool drawhead, bool drawline, double duration)
{
if (state.TimeIsInRange((float)duration))
{
// Set the colors based on the mode
Color lineColor = DirectorStyles.Instance.customSkin.colorEndmarker;
Color headColor = Color.white;
bool canMoveHead = !EditorApplication.isPlaying && state.editSequence.asset.durationMode == TimelineAsset.DurationMode.FixedLength;
if (canMoveHead)
{
if (Event.current.type == EventType.MouseDown)
{
if (m_TimelineDuration.bounds.Contains(Event.current.mousePosition))
{
if (m_PlayHead != null && m_PlayHead.bounds.Contains(Event.current.mousePosition))
{
// ignore duration markers if the mouse is over the TimeCursor.
canMoveHead = false;
}
}
}
}
else
{
lineColor.a *= 0.66f;
headColor = DirectorStyles.Instance.customSkin.colorDuration;
}
if (canMoveHead)
m_TimelineDuration.HandleManipulatorsEvents(state);
m_TimelineDuration.lineColor = lineColor;
m_TimelineDuration.headColor = headColor;
m_TimelineDuration.drawHead = drawhead;
m_TimelineDuration.drawLine = drawline;
m_TimelineDuration.canMoveHead = canMoveHead;
// Draw the TimeAreaItem
// Rect trackheadRect = treeviewBounds;
//trackheadRect.height = clientArea.height;
m_TimelineDuration.Draw(sequenceRect, state, duration);
}
// Draw Blue line in timeline indicating the duration...
if (state.editSequence.asset != null && drawhead)
{
HighlightTimeAreaRange(state.editSequence.GetEvaluableRange(), DirectorStyles.Instance.customSkin.colorDurationLine);
}
}
void HighlightTimeAreaRange(Range range, Color lineColor)
{
if (range.length <= 0.0 || !state.RangeIsVisible(range)) return;
Rect lineRect = Rect.MinMaxRect(
Math.Max(state.TimeToPixel(range.start), state.timeAreaRect.xMin),
state.timeAreaRect.y - DirectorStyles.kDurationGuiThickness + state.timeAreaRect.height,
Math.Min(state.TimeToPixel(range.end), state.timeAreaRect.xMax),
state.timeAreaRect.y + state.timeAreaRect.height);
EditorGUI.DrawRect(lineRect, lineColor);
}
// Drag handler for the gui
void OnTrackDurationDrag(double newTime)
{
if (state.editSequence.asset.durationMode == TimelineAsset.DurationMode.FixedLength && !state.editSequence.isReadOnly)
{
// this is the first call to the drag
if (m_TimelineDuration.firstDrag)
{
TimelineUndo.PushUndo(state.editSequence.asset, "Change Duration");
}
state.editSequence.asset.fixedDuration = newTime;
// when setting a new length, modify the duration of the timeline playable directly instead of
// rebuilding the whole graph
state.UpdateRootPlayableDuration(newTime);
}
m_TimelineDuration.showTooltip = true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b3bd7a976306c9449ba84e0591e8a0f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
private int m_ComponentAddedFrame;
void OnSelectionChangedInactive()
{
// Case 946942 -- when selection changes and the window is open but hidden, timeline
// needs to update selection immediately so preview mode is correctly released
// Case 1123119 -- except when recording
if (!hasFocus)
{
RefreshSelection(!locked && state != null && !state.recording);
}
}
void InitializeEditorCallbacks()
{
Undo.postprocessModifications += PostprocessAnimationRecordingModifications;
Undo.postprocessModifications += ProcessAssetModifications;
Undo.undoRedoPerformed += OnUndoRedo;
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
AnimationUtility.onCurveWasModified += OnCurveModified;
EditorApplication.editorApplicationQuit += OnEditorQuit;
Selection.selectionChanged += OnSelectionChangedInactive;
EditorSceneManager.sceneSaved += OnSceneSaved;
ObjectFactory.componentWasAdded += OnComponentWasAdded;
PrefabUtility.prefabInstanceUpdated += OnPrefabApplied;
}
void OnEditorQuit()
{
TimelineWindowViewPrefs.SaveAll();
}
void RemoveEditorCallbacks()
{
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
Undo.undoRedoPerformed -= OnUndoRedo;
Undo.postprocessModifications -= PostprocessAnimationRecordingModifications;
Undo.postprocessModifications -= ProcessAssetModifications;
AnimationUtility.onCurveWasModified -= OnCurveModified;
EditorApplication.editorApplicationQuit -= OnEditorQuit;
Selection.selectionChanged -= OnSelectionChangedInactive;
EditorSceneManager.sceneSaved -= OnSceneSaved;
ObjectFactory.componentWasAdded -= OnComponentWasAdded;
PrefabUtility.prefabInstanceUpdated -= OnPrefabApplied;
}
// Called when a prefab change is applied to the scene.
// Redraw so control tracks that use prefabs can show changes
void OnPrefabApplied(GameObject go)
{
if (!state.previewMode)
return;
// if we added a component this frame, then rebuild, otherwise just let
// the individual playable handle the prefab application
if (Time.frameCount == m_ComponentAddedFrame)
TimelineEditor.Refresh(RefreshReason.ContentsModified);
else
TimelineEditor.Refresh(RefreshReason.SceneNeedsUpdate);
}
// When the scene is save the director time will get reset.
void OnSceneSaved(Scene scene)
{
if (state != null)
state.OnSceneSaved();
}
void OnCurveModified(AnimationClip clip, EditorCurveBinding binding, AnimationUtility.CurveModifiedType type)
{
InspectorWindow.RepaintAllInspectors();
if (state == null || state.previewMode == false || state.rebuildGraph)
return;
bool hasPlayable = m_PlayableLookup.GetPlayableFromAnimClip(clip, out Playable playable);
// mark the timeline clip as dirty
TimelineClip timelineClip = m_PlayableLookup.GetTimelineClipFromCurves(clip);
if (timelineClip != null)
timelineClip.MarkDirty();
if (type == AnimationUtility.CurveModifiedType.CurveModified)
{
if (hasPlayable)
{
playable.SetAnimatedProperties(clip);
}
// updates the duration of the graph without rebuilding
AnimationUtility.SyncEditorCurves(clip); // deleted keys are not synced when this is sent out, so duration could be incorrect
state.UpdateRootPlayableDuration(state.editSequence.duration);
bool isRecording = TimelineRecording.IsRecordingAnimationTrack;
PlayableDirector masterDirector = TimelineEditor.masterDirector;
bool isGraphValid = masterDirector != null && masterDirector.playableGraph.IsValid();
// don't evaluate if this is caused by recording on an animation track, the extra evaluation can cause hiccups
// Prevent graphs to be resurrected by a changed clip.
if (!isRecording && isGraphValid)
state.Evaluate();
}
else if (EditorUtility.IsDirty(clip)) // curve added/removed, or clip added/removed
{
state.rebuildGraph |= timelineClip != null || hasPlayable;
}
}
void OnPlayModeStateChanged(PlayModeStateChange playModeState)
{
// case 923506 - make sure we save view data before switching modes
if (playModeState == PlayModeStateChange.ExitingEditMode ||
playModeState == PlayModeStateChange.ExitingPlayMode)
TimelineWindowViewPrefs.SaveAll();
bool isPlaymodeAboutToChange = playModeState == PlayModeStateChange.ExitingEditMode || playModeState == PlayModeStateChange.ExitingPlayMode;
// Important to stop the graph on any director so temporary objects are properly cleaned up
if (isPlaymodeAboutToChange && state != null)
state.Stop();
}
UndoPropertyModification[] PostprocessAnimationRecordingModifications(UndoPropertyModification[] modifications)
{
DirtyModifiedObjects(modifications);
if (!state.recording)
return modifications;
var remaining = TimelineRecording.ProcessUndoModification(modifications, state);
// if we've changed, we need to repaint the sequence window to show clip length changes
if (remaining != modifications)
{
// only update if us or the sequencer window has focus
// Prevents color pickers and other dialogs from being wrongly dismissed
bool repaint = (focusedWindow == null) ||
(focusedWindow is InspectorWindow) ||
(focusedWindow is TimelineWindow);
if (repaint)
Repaint();
}
return remaining;
}
void DirtyModifiedObjects(UndoPropertyModification[] modifications)
{
foreach (var m in modifications)
{
if (m.currentValue == null || m.currentValue.target == null)
continue;
var track = m.currentValue.target as TrackAsset;
var playableAsset = m.currentValue.target as PlayableAsset;
var editorClip = m.currentValue.target as EditorClip;
if (track != null)
{
track.MarkDirty();
}
else if (playableAsset != null)
{
var clip = TimelineRecording.FindClipWithAsset(state.editSequence.asset, playableAsset);
if (clip != null)
clip.MarkDirty();
}
else if (editorClip != null && editorClip.clip != null)
{
editorClip.clip.MarkDirty();
}
}
}
UndoPropertyModification[] ProcessAssetModifications(UndoPropertyModification[] modifications)
{
bool rebuildGraph = false;
for (int i = 0; i < modifications.Length && !rebuildGraph; i++)
{
var mod = modifications[i];
// check if an Avatar Mask has been modified
if (mod.previousValue != null && mod.previousValue.target is AvatarMask)
{
rebuildGraph = state.editSequence.asset != null &&
state.editSequence.asset.flattenedTracks
.OfType<UnityEngine.Timeline.AnimationTrack>()
.Any(x => mod.previousValue.target == x.avatarMask);
}
}
if (rebuildGraph)
{
state.rebuildGraph = true;
Repaint();
}
return modifications;
}
void OnUndoRedo()
{
var undos = new List<string>();
var redos = new List<string>();
Undo.GetRecords(undos, redos);
var rebuildAll = redos.Any(x => x.StartsWith("Timeline ")) || undos.Any(x => x.StartsWith("Timeline"));
var evalNow = redos.Any(x => x.Contains("Edit Curve")) || undos.Any(x => x.Contains("Edit Curve"));
if (rebuildAll || evalNow)
{
ValidateSelection();
if (state != null)
{
if (evalNow) // when curves change, the new values need to be set in the transform before the inspector handles the undo
state.EvaluateImmediate();
if (rebuildAll)
state.Refresh();
}
Repaint();
}
}
static void ValidateSelection()
{
//get all the clips in the selection
var selectedClips = Selection.GetFiltered<EditorClip>(SelectionMode.Unfiltered).Select(x => x.clip);
foreach (var selectedClip in selectedClips)
{
var parent = selectedClip.parentTrack;
if (selectedClip.parentTrack != null)
{
if (!parent.clips.Contains(selectedClip))
{
SelectionManager.Remove(selectedClip);
}
}
}
}
void OnComponentWasAdded(Component c)
{
m_ComponentAddedFrame = Time.frameCount;
var go = c.gameObject;
foreach (var seq in state.GetAllSequences())
{
if (seq.director == null || seq.asset == null)
{
return;
}
var rebind = seq.asset.GetOutputTracks().Any(track => seq.director.GetGenericBinding(track) == go);
// Either the playable director has a binding for the GameObject or it is a sibling of the director.
// The second case is needed since we have timeline top level markerTracks that do not have a binding, but
// are still "targeting" the playable director
if (rebind || seq.director.gameObject == go)
{
seq.director.RebindPlayableGraphOutputs();
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1c9c1ed454d0594b951eb6a76ac62ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,511 @@
using System;
using System.Collections.Generic;
using UnityEditor.Experimental.SceneManagement;
using UnityEngine;
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
}
[SerializeField] float m_HierarchySplitterPerc = WindowConstants.hierarchySplitterDefaultPercentage;
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();
int m_SplitterCaptured;
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 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; }
}
float breadCrumbAreaWidth
{
get
{
return state.timeAreaRect.width - WindowConstants.selectorWidth - WindowConstants.cogButtonWidth - WindowConstants.cogButtonPadding;
}
}
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()
{
var rawType = Event.current.rawType; // TODO: rawType seems to be broken after calling Use(), use this Hack and remove it once it's fixed.
var mousePosition = Event.current.mousePosition; // mousePosition is also affected by this bug and does not reflect the original position after a Use()
Initialize();
HandleSplitterResize();
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);
}
m_RectangleSelect.OnGUI(state, rawType, mousePosition);
m_RectangleZoom.OnGUI(state, rawType, mousePosition);
}
void TimelineSectionGUI()
{
GUILayout.BeginVertical();
{
GUILayout.BeginHorizontal(EditorStyles.toolbar, GUILayout.Width(position.width - state.sequencerHeaderWidth));
{
DoSequenceSelectorGUI();
DoBreadcrumbGUI();
OptionsGUI();
}
GUILayout.EndHorizontal();
TimelineGUI();
}
GUILayout.EndVertical();
}
void SplitterGUI()
{
if (!state.IsEditingAnEmptyTimeline())
{
var splitterLineRect = new Rect(state.sequencerHeaderWidth - 1.0f, 0.0f, 2.0f, clientArea.height);
EditorGUI.DrawRect(splitterLineRect, DirectorStyles.Instance.customSkin.colorTopOutline3);
}
}
void TrackViewsGUI()
{
using (new GUIViewportScope(trackRect))
{
TracksGUI(trackRect, state, currentMode.TrackState(state));
}
}
void UserOverlaysGUI()
{
if (Event.current.type != EventType.Repaint)
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 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);
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);
GUILayout.BeginHorizontal();
{
SequencerHeaderGUI();
TimelineSectionGUI();
}
GUILayout.EndHorizontal();
TrackViewsGUI();
MarkerHeaderGUI();
UserOverlaysGUI();
DurationGUI(TimelineItemArea.Lines, duration);
PlayRangeGUI(TimelineItemArea.Lines);
TimeCursorGUI(TimelineItemArea.Lines);
DrawHeaderBackgroundBottomFiller();
SubTimelineRangeGUI();
PlayRangeGUI(TimelineItemArea.Header);
TimeCursorGUI(TimelineItemArea.Header);
SplitterGUI();
}
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 -= state.timeAreaRect.height;
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 HandleSplitterResize()
{
state.mainAreaWidth = position.width;
if (state.editSequence.asset == null)
return;
// Sequencer Header Splitter : The splitter has 6 pixels wide,center it around m_State.sequencerHeaderWidth. That's why there's this -3.
Rect sequencerHeaderSplitterRect = new Rect(state.sequencerHeaderWidth - 3.0f, 0.0f, 6.0f, clientArea.height);
EditorGUIUtility.AddCursorRect(sequencerHeaderSplitterRect, MouseCursor.SplitResizeLeftRight);
if (Event.current.type == EventType.MouseDown)
{
if (sequencerHeaderSplitterRect.Contains(Event.current.mousePosition))
m_SplitterCaptured = 1;
}
if (m_SplitterCaptured > 0)
{
if (Event.current.type == EventType.MouseUp)
{
m_SplitterCaptured = 0;
Event.current.Use();
}
if (Event.current.type == EventType.MouseDrag)
{
if (m_SplitterCaptured == 1)
{
var percInc = Event.current.delta.x / position.width;
m_HierarchySplitterPerc = Mathf.Clamp(m_HierarchySplitterPerc + percInc, WindowConstants.minHierarchySplitter, WindowConstants.maxHierarchySplitter);
state.sequencerHeaderWidth += Event.current.delta.x;
}
Event.current.Use();
}
}
}
void OptionsGUI()
{
if (currentMode.headerState.options == TimelineModeGUIState.Hidden || state.editSequence.asset == null)
return;
using (new EditorGUI.DisabledScope(currentMode.headerState.options == TimelineModeGUIState.Disabled))
{
GUILayout.FlexibleSpace();
if (EditorGUILayout.DropdownButton(DirectorStyles.optionsCogIcon, FocusType.Keyboard, EditorStyles.toolbarButton))
{
GenericMenu menu = new GenericMenu();
menu.AddItem(EditorGUIUtility.TrTextContent("Seconds"), !state.timeInFrames, ChangeTimeCode, "seconds");
menu.AddItem(EditorGUIUtility.TrTextContent("Frames"), state.timeInFrames, ChangeTimeCode, "frames");
menu.AddSeparator("");
TimeAreaContextMenu.AddTimeAreaMenuItems(menu, state);
menu.AddSeparator("");
bool standardFrameRate = false;
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/Film (24)", 24.0f);
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/PAL (25)", 25.0f);
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/NTSC (29.97)", 29.97f);
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/30", 30.0f);
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/50", 50.0f);
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/60", 60.0f);
if (standardFrameRate)
menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Frame Rate/Custom"));
else
menu.AddItem(EditorGUIUtility.TrTextContent("Frame Rate/Custom (" + state.editSequence.frameRate + ")"), true, () => {});
menu.AddSeparator("");
if (state.playRangeEnabled)
{
menu.AddItem(EditorGUIUtility.TrTextContent("Play Range Mode/Loop"), state.playRangeLoopMode, () => state.playRangeLoopMode = true);
menu.AddItem(EditorGUIUtility.TrTextContent("Play Range Mode/Once"), !state.playRangeLoopMode, () => state.playRangeLoopMode = false);
}
else
{
menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Play Range Mode"));
}
menu.AddItem(EditorGUIUtility.TrTextContent("Playback Scrolling mode/None"), state.autoScrollMode == PlaybackScrollMode.None, () => state.autoScrollMode = PlaybackScrollMode.None);
menu.AddItem(EditorGUIUtility.TrTextContent("Playback Scrolling mode/Smooth"), state.autoScrollMode == PlaybackScrollMode.Smooth, () => state.autoScrollMode = PlaybackScrollMode.Smooth);
menu.AddItem(EditorGUIUtility.TrTextContent("Playback Scrolling mode/Pan"), state.autoScrollMode == PlaybackScrollMode.Pan, () => state.autoScrollMode = PlaybackScrollMode.Pan);
menu.AddSeparator("");
menu.AddItem(EditorGUIUtility.TrTextContent("Show Audio Waveforms"), state.showAudioWaveform, () =>
{
state.showAudioWaveform = !state.showAudioWaveform;
});
menu.AddItem(EditorGUIUtility.TrTextContent("Enable Audio Scrubbing"), !state.muteAudioScrubbing, () => state.muteAudioScrubbing = !state.muteAudioScrubbing);
menu.AddSeparator("");
menu.AddItem(EditorGUIUtility.TrTextContent("Snap to Frame"), state.frameSnap, () => state.frameSnap = !state.frameSnap);
menu.AddItem(EditorGUIUtility.TrTextContent("Edge Snap"), state.edgeSnaps, () => state.edgeSnaps = !state.edgeSnaps);
if (Unsupported.IsDeveloperMode())
{
menu.AddItem(EditorGUIUtility.TrTextContent("Show Snapping Debug"), SnapEngine.displayDebugLayout,
() => SnapEngine.displayDebugLayout = !SnapEngine.displayDebugLayout);
menu.AddItem(EditorGUIUtility.TrTextContent("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(EditorGUIUtility.TrTextContent("Edit Skin"), false, () => Selection.activeObject = DirectorStyles.Instance.customSkin);
menu.AddItem(EditorGUIUtility.TrTextContent("Show QuadTree Debugger"), state.showQuadTree,
() => state.showQuadTree = !state.showQuadTree);
}
menu.ShowAsContext();
}
}
}
bool AddStandardFrameRateMenu(GenericMenu menu, string name, float value)
{
bool on = state.editSequence.frameRate.Equals(value);
if (state.editSequence.isReadOnly)
{
menu.AddDisabledItem(EditorGUIUtility.TextContent(name), on);
}
else
{
menu.AddItem(EditorGUIUtility.TextContent(name), on, r =>
{
state.editSequence.frameRate = value;
}, value);
}
return on;
}
void ChangeTimeCode(object obj)
{
string format = obj.ToString();
if (format == "frames")
{
state.timeInFrames = true;
}
else
{
state.timeInFrames = false;
}
}
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
}
);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a956e5803e95df349bd35832492d4014
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,314 @@
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
static readonly GUIContent[] k_TimeReferenceGUIContents =
{
EditorGUIUtility.TrTextContent("Local", "Display time based on the current timeline."),
EditorGUIUtility.TrTextContent("Global", "Display time based on the master timeline.")
};
TimelineMarkerHeaderGUI m_MarkerHeaderGUI;
void SequencerHeaderGUI()
{
using (new EditorGUI.DisabledScope(state.editSequence.asset == null))
{
GUILayout.BeginVertical();
{
TransportToolbarGUI();
GUILayout.BeginHorizontal(GUILayout.Width(sequenceHeaderRect.width));
{
if (state.editSequence.asset != null)
{
GUILayout.Space(DirectorStyles.kBaseIndent);
AddButtonGUI();
GUILayout.FlexibleSpace();
EditModeToolbarGUI(currentMode);
ShowMarkersButton();
EditorGUILayout.Space();
}
}
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
}
void MarkerHeaderGUI()
{
var timelineAsset = state.editSequence.asset;
if (timelineAsset == null)
return;
if (m_MarkerHeaderGUI == null)
m_MarkerHeaderGUI = new TimelineMarkerHeaderGUI(timelineAsset, state);
m_MarkerHeaderGUI.Draw(markerHeaderRect, markerContentRect, state);
}
void TransportToolbarGUI()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar, GUILayout.Width(sequenceHeaderRect.width));
{
using (new EditorGUI.DisabledScope(currentMode.PreviewState(state) == TimelineModeGUIState.Disabled))
{
PreviewModeButtonGUI();
}
using (new EditorGUI.DisabledScope(currentMode.ToolbarState(state) == TimelineModeGUIState.Disabled))
{
GotoBeginingSequenceGUI();
PreviousEventButtonGUI();
PlayButtonGUI();
NextEventButtonGUI();
GotoEndSequenceGUI();
GUILayout.Space(10.0f);
PlayRangeButtonGUI();
GUILayout.FlexibleSpace();
TimeCodeGUI();
ReferenceTimeGUI();
}
}
GUILayout.EndHorizontal();
}
void PreviewModeButtonGUI()
{
EditorGUI.BeginChangeCheck();
var enabled = state.previewMode;
enabled = GUILayout.Toggle(enabled, DirectorStyles.previewContent, EditorStyles.toolbarButton);
if (EditorGUI.EndChangeCheck())
{
// turn off auto play as well, so it doesn't auto reenable
if (!enabled)
{
state.SetPlaying(false);
state.recording = false;
}
state.previewMode = enabled;
// if we are successfully enabled, rebuild the graph so initial states work correctly
// Note: testing both values because previewMode setter can "fail"
if (enabled && state.previewMode)
state.rebuildGraph = true;
}
}
void GotoBeginingSequenceGUI()
{
if (GUILayout.Button(DirectorStyles.gotoBeginingContent, EditorStyles.toolbarButton))
{
state.editSequence.time = 0;
state.EnsurePlayHeadIsVisible();
}
}
// in the editor the play button starts/stops simulation
void PlayButtonGUIEditor()
{
EditorGUI.BeginChangeCheck();
var isPlaying = GUILayout.Toggle(state.playing, DirectorStyles.playContent, EditorStyles.toolbarButton);
if (EditorGUI.EndChangeCheck())
{
state.SetPlaying(isPlaying);
}
}
// in playmode the button reflects the playing state.
// needs to disabled if playing is not possible
void PlayButtonGUIPlayMode()
{
bool buttonEnabled = state.masterSequence.director != null &&
state.masterSequence.director.isActiveAndEnabled;
using (new EditorGUI.DisabledScope(!buttonEnabled))
{
PlayButtonGUIEditor();
}
}
void PlayButtonGUI()
{
if (!Application.isPlaying)
PlayButtonGUIEditor();
else
PlayButtonGUIPlayMode();
}
void NextEventButtonGUI()
{
if (GUILayout.Button(DirectorStyles.nextFrameContent, EditorStyles.toolbarButton))
{
state.referenceSequence.frame += 1;
}
}
void PreviousEventButtonGUI()
{
if (GUILayout.Button(DirectorStyles.previousFrameContent, EditorStyles.toolbarButton))
{
state.referenceSequence.frame -= 1;
}
}
void GotoEndSequenceGUI()
{
if (GUILayout.Button(DirectorStyles.gotoEndContent, EditorStyles.toolbarButton))
{
state.editSequence.time = state.editSequence.asset.duration;
state.EnsurePlayHeadIsVisible();
}
}
void PlayRangeButtonGUI()
{
using (new EditorGUI.DisabledScope(EditorApplication.isPlaying || state.IsEditingASubTimeline()))
{
state.playRangeEnabled = GUILayout.Toggle(state.playRangeEnabled, DirectorStyles.Instance.playrangeContent, EditorStyles.toolbarButton);
}
}
void AddButtonGUI()
{
if (currentMode.trackOptionsState.newButton == TimelineModeGUIState.Hidden)
return;
using (new EditorGUI.DisabledScope(currentMode.trackOptionsState.newButton == TimelineModeGUIState.Disabled))
{
if (EditorGUILayout.DropdownButton(DirectorStyles.newContent, FocusType.Passive, EditorStyles.toolbarPopup))
{
// if there is 1 and only 1 track selected, AND it's a group, add to that group
var groupTracks = SelectionManager.SelectedTracks().ToList();
if (groupTracks.Any(x => x.GetType() != typeof(GroupTrack) || x.lockedInHierarchy))
groupTracks = null;
SequencerContextMenu.ShowNewTracksContextMenu(groupTracks, state, EditorGUILayout.s_LastRect);
}
}
}
void ShowMarkersButton()
{
var asset = state.editSequence.asset;
if (asset == null)
return;
var content = state.showMarkerHeader ? DirectorStyles.showMarkersOn : DirectorStyles.showMarkersOff;
SetShowMarkerHeader(GUILayout.Toggle(state.showMarkerHeader, content, DirectorStyles.Instance.showMarkersBtn));
}
internal void SetShowMarkerHeader(bool newValue)
{
if (state.showMarkerHeader == newValue)
return;
TimelineUndo.PushUndo(state.editSequence.viewModel, "Toggle Show Markers");
state.showMarkerHeader = newValue;
if (!newValue)
{
var asset = state.editSequence.asset;
if (asset != null && asset.markerTrack != null)
{
SelectionManager.Remove(asset.markerTrack);
foreach (var marker in asset.markerTrack.GetMarkers())
{
SelectionManager.Remove(marker);
}
}
}
}
internal void SetShowTrackMarkers(TrackAsset track, bool showMarkerHeader)
{
var currentValue = track.GetShowMarkers();
if (currentValue != showMarkerHeader)
{
TimelineUndo.PushUndo(state.editSequence.viewModel, "Toggle Show Markers");
track.SetShowMarkers(showMarkerHeader);
if (!showMarkerHeader)
{
foreach (var marker in track.GetMarkers())
{
SelectionManager.Remove(marker);
}
}
}
}
static void EditModeToolbarGUI(TimelineMode mode)
{
using (new EditorGUI.DisabledScope(mode.EditModeButtonsState(instance.state) == TimelineModeGUIState.Disabled))
{
var editType = EditMode.editType;
using (var checkScope = new EditorGUI.ChangeCheckScope())
{
var icon = editType == EditMode.EditType.Mix ? DirectorStyles.mixOn : DirectorStyles.mixOff;
GUILayout.Toggle(editType == EditMode.EditType.Mix, icon, DirectorStyles.Instance.editModeBtn);
if (checkScope.changed)
EditMode.editType = EditMode.EditType.Mix;
}
using (var checkScope = new EditorGUI.ChangeCheckScope())
{
var icon = editType == EditMode.EditType.Ripple ? DirectorStyles.rippleOn : DirectorStyles.rippleOff;
GUILayout.Toggle(editType == EditMode.EditType.Ripple, icon, DirectorStyles.Instance.editModeBtn);
if (checkScope.changed)
EditMode.editType = EditMode.EditType.Ripple;
}
using (var checkScope = new EditorGUI.ChangeCheckScope())
{
var icon = editType == EditMode.EditType.Replace ? DirectorStyles.replaceOn : DirectorStyles.replaceOff;
GUILayout.Toggle(editType == EditMode.EditType.Replace, icon, DirectorStyles.Instance.editModeBtn);
if (checkScope.changed)
EditMode.editType = EditMode.EditType.Replace;
}
}
}
// Draws the box to enter the time field
void TimeCodeGUI()
{
EditorGUI.BeginChangeCheck();
var currentTime = state.editSequence.asset != null ? TimeReferenceUtility.ToTimeString(state.editSequence.time, "F1") : "0";
var r = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.toolbarTextField, GUILayout.MinWidth(WindowConstants.minTimeCodeWidth));
var id = GUIUtility.GetControlID("RenameFieldTextField".GetHashCode(), FocusType.Passive, r);
var newCurrentTime = EditorGUI.DelayedTextFieldInternal(r, id, GUIContent.none, currentTime, null, EditorStyles.toolbarTextField);
if (EditorGUI.EndChangeCheck())
state.editSequence.time = TimeReferenceUtility.FromTimeString(newCurrentTime);
}
void ReferenceTimeGUI()
{
if (!state.IsEditingASubTimeline())
return;
EditorGUI.BeginChangeCheck();
var rect = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.toolbarButton, GUILayout.Width(WindowConstants.refTimeWidth));
state.timeReferenceMode = (TimeReferenceMode)EditorGUI.CycleButton(rect, (int)state.timeReferenceMode, k_TimeReferenceGUIContents, EditorStyles.toolbarButtonRight);
if (EditorGUI.EndChangeCheck())
OnTimeReferenceModeChanged();
}
void OnTimeReferenceModeChanged()
{
m_TimeAreaDirty = true;
InitTimeAreaFrameRate();
SyncTimeAreaShownRange();
foreach (var inspector in InspectorWindow.GetAllInspectorWindows())
{
inspector.Repaint();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fdd19c82588da3e498a0c98951efa6c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,41 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
readonly Control m_PreTreeViewControl = new Control();
readonly Control m_PostTreeViewControl = new Control();
readonly RectangleSelect m_RectangleSelect = new RectangleSelect();
readonly RectangleZoom m_RectangleZoom = new RectangleZoom();
void InitializeManipulators()
{
// Order is important!
// Manipulators that needs to be processed BEFORE the treeView (mainly anything clip related)
m_PreTreeViewControl.AddManipulator(new TimelinePanManipulator());
m_PreTreeViewControl.AddManipulator(new InlineCurveResize());
m_PreTreeViewControl.AddManipulator(new TrackZoom());
m_PreTreeViewControl.AddManipulator(new Jog());
m_PreTreeViewControl.AddManipulator(TimelineZoomManipulator.Instance);
m_PreTreeViewControl.AddManipulator(new ContextMenuManipulator());
m_PreTreeViewControl.AddManipulator(new TimelineMarkerHeaderContextMenu());
m_PreTreeViewControl.AddManipulator(new EaseClip());
m_PreTreeViewControl.AddManipulator(new TrimClip());
m_PreTreeViewControl.AddManipulator(new SelectAndMoveItem());
m_PreTreeViewControl.AddManipulator(new TrackDoubleClick());
m_PreTreeViewControl.AddManipulator(new DrillIntoClip());
m_PreTreeViewControl.AddManipulator(new ItemActionShortcutManipulator());
m_PreTreeViewControl.AddManipulator(new InlineCurvesShortcutManipulator());
// Manipulators that needs to be processed AFTER the treeView or any GUI element able to use event (like inline curves)
m_PostTreeViewControl.AddManipulator(new TimeAreaContextMenu());
m_PostTreeViewControl.AddManipulator(new TrackShortcutManipulator());
m_PostTreeViewControl.AddManipulator(new TimelineShortcutManipulator());
m_PostTreeViewControl.AddManipulator(new ClearSelection());
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3a595c9a8ed19040bb2612fe168759d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,131 @@
using System.Linq;
using UnityEngine;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
TimeAreaItem m_PlayRangeEnd;
TimeAreaItem m_PlayRangeStart;
void PlayRangeGUI(TimelineItemArea area)
{
if (!currentMode.ShouldShowPlayRange(state) || treeView == null)
return;
if (state.masterSequence.asset != null && !state.masterSequence.asset.GetRootTracks().Any())
return;
// left Time Cursor
if (m_PlayRangeStart == null || m_PlayRangeStart.style != styles.playTimeRangeStart)
{
m_PlayRangeStart = new TimeAreaItem(styles.playTimeRangeStart, OnTrackHeadMinSelectDrag);
Vector2 offset = new Vector2(-2.0f, 0);
m_PlayRangeStart.boundOffset = offset;
}
// right Time Cursor
if (m_PlayRangeEnd == null || m_PlayRangeEnd.style != styles.playTimeRangeEnd)
{
m_PlayRangeEnd = new TimeAreaItem(styles.playTimeRangeEnd, OnTrackHeadMaxSelectDrag);
Vector2 offset = new Vector2(2.0f, 0);
m_PlayRangeEnd.boundOffset = offset;
}
if (area == TimelineItemArea.Header)
DrawPlayRange(true, false);
else if (area == TimelineItemArea.Lines)
DrawPlayRange(false, true);
}
void DrawPlayRange(bool drawHeads, bool drawLines)
{
Rect timeCursorRect = state.timeAreaRect;
timeCursorRect.height = clientArea.height;
m_PlayRangeEnd.HandleManipulatorsEvents(state);
m_PlayRangeStart.HandleManipulatorsEvents(state);
// The first time a user enable the play range, we put the play range 75% around the current time...
if (state.playRange == TimelineAssetViewModel.NoPlayRangeSet)
{
float minimumPlayRangeTime = 0.01f;
float t0 = Mathf.Max(0.0f, state.PixelToTime(state.timeAreaRect.xMin));
float t1 = Mathf.Min((float)state.masterSequence.duration, state.PixelToTime(state.timeAreaRect.xMax));
if (Mathf.Abs(t1 - t0) <= minimumPlayRangeTime)
{
state.playRange = new Vector2(t0, t1);
return;
}
float deltaT = (t1 - t0) * 0.25f / 2.0f;
t0 += deltaT;
t1 -= deltaT;
if (t1 < t0)
{
float temp = t0;
t0 = t1;
t1 = temp;
}
if (Mathf.Abs(t1 - t0) < minimumPlayRangeTime)
{
if (t0 - minimumPlayRangeTime > 0.0f)
t0 -= minimumPlayRangeTime;
else if (t1 + minimumPlayRangeTime < state.masterSequence.duration)
t1 += minimumPlayRangeTime;
}
state.playRange = new Vector2(t0, t1);
}
// Draw the head or the lines according to the parameters..
m_PlayRangeStart.drawHead = drawHeads;
m_PlayRangeStart.drawLine = drawLines;
m_PlayRangeEnd.drawHead = drawHeads;
m_PlayRangeEnd.drawLine = drawLines;
var playRangeTime = state.playRange;
m_PlayRangeStart.Draw(sequenceContentRect, state, playRangeTime.x);
m_PlayRangeEnd.Draw(sequenceContentRect, state, playRangeTime.y);
// Draw Time Range Box from Start to End...
if (state.playRangeEnabled && m_PlayHead != null)
{
Rect rect =
Rect.MinMaxRect(
Mathf.Clamp(state.TimeToPixel(playRangeTime.x), state.timeAreaRect.xMin, state.timeAreaRect.xMax),
m_PlayHead.bounds.yMax,
Mathf.Clamp(state.TimeToPixel(playRangeTime.y), state.timeAreaRect.xMin, state.timeAreaRect.xMax),
sequenceContentRect.height + state.timeAreaRect.height + timeCursorRect.y
);
EditorGUI.DrawRect(rect, DirectorStyles.Instance.customSkin.colorRange);
rect.height = 3f;
EditorGUI.DrawRect(rect, Color.white);
}
}
void OnTrackHeadMinSelectDrag(double newTime)
{
Vector2 range = state.playRange;
range.x = (float)newTime;
state.playRange = range;
m_PlayRangeStart.showTooltip = true;
}
void OnTrackHeadMaxSelectDrag(double newTime)
{
Vector2 range = state.playRange;
range.y = (float)newTime;
state.playRange = range;
m_PlayRangeEnd.showTooltip = true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14d748c963c7b3549bed45457cc92c4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,79 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
PlayableLookup m_PlayableLookup = new PlayableLookup();
class PlayableLookup
{
const int k_InitialDictionarySize = 10;
readonly Dictionary<AnimationClip, Playable> m_AnimationClipToPlayable =
new Dictionary<AnimationClip, Playable>(k_InitialDictionarySize);
readonly Dictionary<AnimationClip, TimelineClip> m_AnimationClipToTimelineClip =
new Dictionary<AnimationClip, TimelineClip>(k_InitialDictionarySize);
public void UpdatePlayableLookup(TimelineClip clip, GameObject go, Playable p)
{
if (clip == null || go == null || !p.IsValid())
return;
if (clip.curves != null)
m_AnimationClipToTimelineClip[clip.curves] = clip;
UpdatePlayableLookup(clip.parentTrack.timelineAsset, clip, go, p);
}
public void UpdatePlayableLookup(TrackAsset track, GameObject go, Playable p)
{
if (track == null || go == null || !p.IsValid())
return;
UpdatePlayableLookup(track.timelineAsset, track, go, p);
}
void UpdatePlayableLookup(TimelineAsset timelineAsset, ICurvesOwner curvesOwner, GameObject go, Playable p)
{
var director = go.GetComponent<PlayableDirector>();
var editingDirector = instance.state.editSequence.director;
// No Asset mode update
if (curvesOwner.curves != null && director != null && director == editingDirector &&
timelineAsset == instance.state.editSequence.asset)
{
m_AnimationClipToPlayable[curvesOwner.curves] = p;
}
}
public bool GetPlayableFromAnimClip(AnimationClip clip, out Playable p)
{
if (clip == null)
{
p = Playable.Null;
return false;
}
return m_AnimationClipToPlayable.TryGetValue(clip, out p);
}
public TimelineClip GetTimelineClipFromCurves(AnimationClip clip)
{
if (clip == null)
return null;
TimelineClip timelineClip = null;
m_AnimationClipToTimelineClip.TryGetValue(clip, out timelineClip);
return timelineClip;
}
public void ClearPlayableLookup()
{
m_AnimationClipToPlayable.Clear();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3168bf9e060ff4b46be4bf08e308ce97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,87 @@
using System;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
void OnPreviewPlayModeChanged(bool isPlaying)
{
if (EditorApplication.isPlaying)
return;
if (isPlaying)
{
PreparePreviewPlay();
EditorApplication.update += OnPreviewPlay;
}
else
{
EditorApplication.update -= OnPreviewPlay;
}
}
void PreparePreviewPlay()
{
if (state == null || state.masterSequence.asset == null || state.masterSequence.director == null)
return;
if (state.playRangeEnabled && !state.isJogging)
{
EnsurePlayRangeIsRespected();
}
}
internal void OnPreviewPlay()
{
if (state == null || state.masterSequence.asset == null || state.masterSequence.director == null)
return;
var director = state.masterSequence.director;
if (director.timeUpdateMode == DirectorUpdateMode.Manual)
{
Repaint();
return;
}
if (state.isJogging)
{
ApplyJog();
}
else if (state.playRangeEnabled)
{
EnsurePlayRangeIsRespected();
}
if (director.extrapolationMode == DirectorWrapMode.None && director.playableGraph.IsValid() && !state.playRangeEnabled && director.playableGraph.IsDone())
{
//reset time if we hit the end of the timeline
state.masterSequence.time = 0.0;
state.Pause();
}
Repaint();
AudioMixerWindow.RepaintAudioMixerWindow();
}
void ApplyJog()
{
state.masterSequence.time = Math.Max(0.0, Math.Min(state.masterSequence.duration, state.masterSequence.time + state.playbackSpeed));
}
void EnsurePlayRangeIsRespected()
{
var playRangeTime = state.playRange;
var time = state.masterSequence.time;
if (Math.Abs(time - playRangeTime.y) < TimeUtility.kFrameRateEpsilon || time > playRangeTime.y || time < playRangeTime.x)
{
state.masterSequence.time = playRangeTime.x;
// case 1215926 : Special case to make the director mode to play if the wrap mode is None.
// In that mode, the engine stop the graph before we can ensure play range is respected.
if (!state.playing && state.masterSequence.director.extrapolationMode == DirectorWrapMode.None)
state.Play();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c9c9f62af2efb948a1974650039e2db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,80 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
[SerializeField]
SequencePath m_SequencePath;
private Object lastSelectedGO { get; set; }
void OnSelectionChange()
{
RefreshSelection(false);
}
void RefreshSelection(bool forceRebuild)
{
// if we're in Locked mode, keep current selection - don't use locked property because the
// sequence hierarchy may need to be rebuilt and it assumes no asset == unlocked
if (m_LockTracker.isLocked || (state != null && state.recording))
{
RestoreLastSelection(forceRebuild);
return;
}
// selection is a TimelineAsset
Object selectedObject = Selection.activeObject as TimelineAsset;
if (selectedObject != null)
{
SetCurrentSelection(Selection.activeObject);
return;
}
// selection is a GameObject, or a prefab with a director
var selectedGO = Selection.activeGameObject;
if (selectedGO != null)
{
bool isSceneObject = !PrefabUtility.IsPartOfPrefabAsset(selectedGO);
bool hasDirector = selectedGO.GetComponent<PlayableDirector>() != null;
if (isSceneObject || hasDirector)
{
SetCurrentSelection(selectedGO);
return;
}
}
// otherwise, keep the same selection.
RestoreLastSelection(forceRebuild);
}
void RestoreLastSelection(bool forceRebuild)
{
state.SetCurrentSequencePath(m_SequencePath, forceRebuild);
}
void SetCurrentSelection(Object obj)
{
var selectedGameObject = obj as GameObject;
if (selectedGameObject != null)
{
PlayableDirector director = TimelineUtility.GetDirectorComponentForGameObject(selectedGameObject);
SetCurrentTimeline(director);
lastSelectedGO = selectedGameObject;
}
else
{
var selectedSequenceAsset = obj as TimelineAsset;
if (selectedSequenceAsset != null)
{
SetCurrentTimeline(selectedSequenceAsset);
lastSelectedGO = selectedGameObject;
}
}
Repaint();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16548db454f7a3344b41ca2e5cdb52b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,53 @@
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
void InitializeStateChange()
{
state.OnPlayStateChange += OnPreviewPlayModeChanged;
state.OnDirtyStampChange += OnStateChange;
state.OnBeforeSequenceChange += OnBeforeSequenceChange;
state.OnAfterSequenceChange += OnAfterSequenceChange;
state.OnRebuildGraphChange += () =>
{
// called when the graph is rebuild, since the UI tree isn't necessarily rebuilt.
if (!state.rebuildGraph)
{
// send callbacks to the tacks
if (treeView != null)
{
var allTrackGuis = treeView.allTrackGuis;
if (allTrackGuis != null)
{
for (int i = 0; i < allTrackGuis.Count; i++)
allTrackGuis[i].OnGraphRebuilt();
}
}
}
};
state.OnTimeChange += () =>
{
if (EditorApplication.isPlaying == false)
{
state.UpdateRecordingState();
EditorApplication.SetSceneRepaintDirty();
}
// the time is sync'd prior to the callback
state.Evaluate(); // will do the repaint
InspectorWindow.RepaintAllInspectors();
};
state.OnRecordingChange += () =>
{
if (!state.recording)
{
TrackAssetRecordingExtensions.ClearRecordingState();
}
};
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b926af22079e00e4a8c073321194cea1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,107 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
[NonSerialized] TimelineTimeArea m_TimeArea;
public TimeArea timeArea { get { return m_TimeArea; } }
internal static class Styles
{
public static string DurationModeText = L10n.Tr("Duration Mode/{0}");
}
float m_LastFrameRate;
bool m_TimeAreaDirty = true;
void InitializeTimeArea()
{
if (m_TimeArea == null)
{
m_TimeArea = new TimelineTimeArea(state, false)
{
hRangeLocked = false,
vRangeLocked = true,
margin = 10,
scaleWithWindow = true,
hSlider = true,
vSlider = false,
hBaseRangeMin = 0.0f,
hBaseRangeMax = WindowState.kMaxShownTime,
hRangeMin = 0.0f,
hScaleMax = WindowConstants.maxTimeAreaScaling,
rect = state.timeAreaRect
};
m_TimeAreaDirty = true;
InitTimeAreaFrameRate();
SyncTimeAreaShownRange();
}
}
void TimelineGUI()
{
if (!currentMode.ShouldShowTimeArea(state))
return;
Rect rect = state.timeAreaRect;
m_TimeArea.rect = new Rect(rect.x, rect.y, rect.width, clientArea.height - rect.y);
if (m_LastFrameRate != state.referenceSequence.frameRate)
InitTimeAreaFrameRate();
SyncTimeAreaShownRange();
m_TimeArea.BeginViewGUI();
m_TimeArea.TimeRuler(rect, state.referenceSequence.frameRate, true, false, 1.0f, state.timeInFrames ? TimeArea.TimeFormat.Frame : TimeArea.TimeFormat.TimeFrame);
m_TimeArea.EndViewGUI();
}
void InitTimeAreaFrameRate()
{
m_LastFrameRate = state.referenceSequence.frameRate;
m_TimeArea.hTicks.SetTickModulosForFrameRate(m_LastFrameRate);
}
void SyncTimeAreaShownRange()
{
var range = state.timeAreaShownRange;
if (!Mathf.Approximately(range.x, m_TimeArea.shownArea.x) || !Mathf.Approximately(range.y, m_TimeArea.shownArea.xMax))
{
// set view data onto the time area
if (m_TimeAreaDirty)
{
m_TimeArea.SetShownHRange(range.x, range.y);
m_TimeAreaDirty = false;
}
else
{
// set time area data onto the view data
state.TimeAreaChanged();
}
}
m_TimeArea.hBaseRangeMax = (float)state.editSequence.duration;
}
class TimelineTimeArea : TimeArea
{
readonly WindowState m_State;
public TimelineTimeArea(WindowState state, bool minimalGUI) : base(minimalGUI)
{
m_State = state;
}
public override string FormatTickTime(float time, float frameRate, TimeFormat timeFormat)
{
time = m_State.timeReferenceMode == TimeReferenceMode.Global ?
(float)m_State.editSequence.ToGlobalTime(time) : time;
return FormatTime(time, frameRate, timeFormat);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b846f69b139b3a341a5699a09fa52b2c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,83 @@
using System;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
TimeAreaItem m_PlayHead;
void TimeCursorGUI(TimelineItemArea area)
{
DrawTimeOnSlider();
if (!CanDrawTimeCursor(area))
return;
if (m_PlayHead == null || m_PlayHead.style != styles.timeCursor)
{
m_PlayHead = new TimeAreaItem(styles.timeCursor, OnTrackHeadDrag);
m_PlayHead.AddManipulator(new PlayheadContextMenu(m_PlayHead));
}
var headerMode = area == TimelineItemArea.Header;
DrawTimeCursor(headerMode, !headerMode);
}
bool CanDrawTimeCursor(TimelineItemArea area)
{
if (!currentMode.ShouldShowTimeCursor(state))
return false;
if (treeView == null || state.editSequence.asset == null || (state.editSequence.asset != null && state.IsEditingAnEmptyTimeline()))
return false;
if (area == TimelineItemArea.Lines && !state.TimeIsInRange((float)state.editSequence.time))
return false;
return true;
}
void DrawTimeOnSlider()
{
if (currentMode.ShouldShowTimeCursor(state))
{
var colorDimFactor = EditorGUIUtility.isProSkin ? 0.7f : 0.9f;
var c = styles.timeCursor.normal.textColor * colorDimFactor;
float time = Mathf.Max((float)state.editSequence.time, 0);
float duration = (float)state.editSequence.duration;
m_TimeArea.DrawTimeOnSlider(time, c, duration, DirectorStyles.kDurationGuiThickness);
}
}
void DrawTimeCursor(bool drawHead, bool drawline)
{
m_PlayHead.HandleManipulatorsEvents(state);
if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
{
if (state.timeAreaRect.Contains(Event.current.mousePosition))
{
state.SetPlaying(false);
m_PlayHead.HandleManipulatorsEvents(state);
state.editSequence.time = Math.Max(0.0, state.GetSnappedTimeAtMousePosition(Event.current.mousePosition));
}
}
state.isClipSnapping = false;
m_PlayHead.drawLine = drawline;
m_PlayHead.drawHead = drawHead;
m_PlayHead.Draw(sequenceContentRect, state, state.editSequence.time);
}
void OnTrackHeadDrag(double newTime)
{
state.editSequence.time = Math.Max(0.0, newTime);
m_PlayHead.showTooltip = true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e763a90581e2d8143bc9a0e384ce6f0f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
using UnityEngine.Playables;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
public TimelineTreeViewGUI treeView { get; private set; }
void TracksGUI(Rect clientRect, WindowState state, TimelineModeGUIState trackState)
{
if (Event.current.type == EventType.Repaint && treeView != null)
{
state.spacePartitioner.Clear();
}
if (state.IsEditingASubTimeline() && !state.IsEditingAnEmptyTimeline())
{
var headerRect = clientRect;
headerRect.width = state.sequencerHeaderWidth;
Graphics.DrawBackgroundRect(state, headerRect);
var clipRect = clientRect;
clipRect.xMin = headerRect.xMax;
Graphics.DrawBackgroundRect(state, clipRect, subSequenceMode: true);
}
else
{
Graphics.DrawBackgroundRect(state, clientRect);
}
if (!state.IsEditingAnEmptyTimeline())
m_TimeArea.DrawMajorTicks(sequenceContentRect, state.referenceSequence.frameRate);
GUILayout.BeginVertical();
{
GUILayout.Space(5.0f);
GUILayout.BeginHorizontal();
if (this.state.editSequence.asset == null)
DrawNoSequenceGUI(state);
else
DrawTracksGUI(clientRect, trackState);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
Graphics.DrawShadow(clientRect);
}
void DrawNoSequenceGUI(WindowState windowState)
{
bool showCreateButton = false;
var currentlySelectedGo = UnityEditor.Selection.activeObject != null ? UnityEditor.Selection.activeObject as GameObject : null;
var textContent = DirectorStyles.noTimelineAssetSelected;
var existingDirector = currentlySelectedGo != null ? currentlySelectedGo.GetComponent<PlayableDirector>() : null;
var existingAsset = existingDirector != null ? existingDirector.playableAsset : null;
if (currentlySelectedGo != null && !TimelineUtility.IsPrefabOrAsset(currentlySelectedGo) && existingAsset == null)
{
showCreateButton = true;
textContent = new GUIContent(String.Format(DirectorStyles.createTimelineOnSelection.text, currentlySelectedGo.name, "a Director component and a Timeline asset"));
}
GUILayout.FlexibleSpace();
GUILayout.BeginVertical();
GUILayout.FlexibleSpace();
GUILayout.Label(textContent);
if (showCreateButton)
{
GUILayout.BeginHorizontal();
var textSize = GUI.skin.label.CalcSize(textContent);
GUILayout.Space((textSize.x / 2.0f) - (WindowConstants.createButtonWidth / 2.0f));
if (GUILayout.Button("Create", GUILayout.Width(WindowConstants.createButtonWidth)))
{
var message = DirectorStyles.createNewTimelineText.text + " '" + currentlySelectedGo.name + "'";
string newSequencePath = EditorUtility.SaveFilePanelInProject(DirectorStyles.createNewTimelineText.text, currentlySelectedGo.name + "Timeline", "playable", message, ProjectWindowUtil.GetActiveFolderPath());
if (!string.IsNullOrEmpty(newSequencePath))
{
var newAsset = CreateInstance<TimelineAsset>();
AssetDatabase.CreateAsset(newAsset, newSequencePath);
Undo.IncrementCurrentGroup();
if (existingDirector == null)
{
existingDirector = Undo.AddComponent<PlayableDirector>(currentlySelectedGo);
}
existingDirector.playableAsset = newAsset;
SetCurrentTimeline(existingDirector);
var newTrack = TimelineHelpers.CreateTrack<AnimationTrack>();
windowState.previewMode = false;
TimelineUtility.SetSceneGameObject(windowState.editSequence.director, newTrack, currentlySelectedGo);
}
// If we reach this point, the state of the pannel has changed; skip the rest of this GUI phase
// Fixes: case 955831 - [OSX] NullReferenceException when creating a timeline on a selected object
GUIUtility.ExitGUI();
}
GUILayout.EndHorizontal();
}
GUILayout.FlexibleSpace();
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
}
public enum OverlayDataTypes
{
None,
BackgroundColor,
BackgroundTexture,
TextBox
}
public struct OverlayData
{
public OverlayDataTypes types { get; private set; }
public Rect rect { get; internal set; }
public string text { get; private set; }
public Texture2D texture { get; private set; }
public Color color { get; private set; }
public GUIStyle backgroundTextStyle { get; private set; }
public GUIStyle textStyle { get; private set; }
public static OverlayData CreateColorOverlay(Rect rectangle, Color backgroundColor)
{
OverlayData data = new OverlayData();
data.rect = rectangle;
data.color = backgroundColor;
data.types = OverlayDataTypes.BackgroundColor;
return data;
}
public static OverlayData CreateTextureOverlay(Rect rectangle, Texture2D backTexture)
{
OverlayData data = new OverlayData();
data.rect = rectangle;
data.texture = backTexture;
data.types = OverlayDataTypes.BackgroundTexture;
return data;
}
public static OverlayData CreateTextBoxOverlay(Rect rectangle, string msg, GUIStyle textstyle, Color textcolor, Color bgTextColor, GUIStyle bgTextStyle)
{
OverlayData data = new OverlayData();
data.rect = rectangle;
data.text = msg;
data.textStyle = textstyle;
data.textStyle.normal.textColor = textcolor;
data.backgroundTextStyle = bgTextStyle;
data.backgroundTextStyle.normal.textColor = bgTextColor;
data.types = OverlayDataTypes.TextBox;
return data;
}
}
internal List<OverlayData> OverlayDrawData = new List<OverlayData>();
void DrawTracksGUI(Rect clientRect, TimelineModeGUIState trackState)
{
GUILayout.BeginVertical(GUILayout.Height(clientRect.height));
if (treeView != null)
{
if (Event.current.type == EventType.Layout)
{
OverlayDrawData.Clear();
}
treeView.OnGUI(clientRect);
if (Event.current.type == EventType.Repaint)
{
foreach (var overlayData in OverlayDrawData)
{
using (new GUIViewportScope(sequenceContentRect))
DrawOverlay(overlayData);
}
}
}
GUILayout.EndVertical();
}
void DrawOverlay(OverlayData overlayData)
{
Rect overlayRect = GUIClip.Clip(overlayData.rect);
if (overlayData.types == OverlayDataTypes.BackgroundColor)
{
EditorGUI.DrawRect(overlayRect, overlayData.color);
}
else if (overlayData.types == OverlayDataTypes.BackgroundTexture)
{
Graphics.DrawTextureRepeated(overlayRect, overlayData.texture);
}
else if (overlayData.types == OverlayDataTypes.TextBox)
{
using (new GUIColorOverride(overlayData.backgroundTextStyle.normal.textColor))
GUI.Box(overlayRect, GUIContent.none, overlayData.backgroundTextStyle);
Graphics.ShadowLabel(overlayRect, GUIContent.Temp(overlayData.text), overlayData.textStyle, overlayData.textStyle.normal.textColor, Color.black);
}
}
void RefreshInlineCurves()
{
foreach (var trackGUI in allTracks.OfType<TimelineTrackGUI>())
{
if (trackGUI.inlineCurveEditor != null)
trackGUI.inlineCurveEditor.Refresh();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08d23c0b73905c148b525c3c93fff580
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9d7bb79ed0c2854a8a5ed7decc3e44f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,119 @@
using System;
using System.IO;
using UnityEditorInternal;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class ScriptableObjectViewPrefs<TViewModel> : IDisposable where TViewModel : ScriptableObject
{
const string k_DefaultFilePath = "Library/";
const string k_Extension = ".pref";
readonly string m_RelativePath;
readonly string m_AbsolutePath;
readonly string m_FileName;
ScriptableObject m_Asset;
TViewModel m_ViewModel;
bool isSavable
{
get
{
return m_Asset != null &&
m_ViewModel != null &&
!string.IsNullOrEmpty(m_FileName);
}
}
public ScriptableObjectViewPrefs(ScriptableObject asset, string relativeSavePath)
{
m_Asset = asset;
m_RelativePath = string.IsNullOrEmpty(relativeSavePath) ? k_DefaultFilePath : relativeSavePath;
if (!m_RelativePath.EndsWith("/", StringComparison.Ordinal))
m_RelativePath += "/";
m_AbsolutePath = Application.dataPath + "/../" + m_RelativePath;
var assetKey = GetAssetKey(asset);
m_FileName = string.IsNullOrEmpty(assetKey) ? string.Empty : assetKey + k_Extension;
}
public TViewModel viewModel
{
get
{
if (m_ViewModel == null)
{
if (m_Asset == null)
m_ViewModel = CreateViewModel();
else
m_ViewModel = LoadViewModel() ?? CreateViewModel();
}
return m_ViewModel;
}
}
public void Save()
{
if (!isSavable)
return;
// make sure the path exists or file write will fail
if (!Directory.Exists(m_AbsolutePath))
Directory.CreateDirectory(m_AbsolutePath);
const bool saveAsText = true;
InternalEditorUtility.SaveToSerializedFileAndForget(new UnityObject[] { m_ViewModel }, m_RelativePath + m_FileName, saveAsText);
}
public void DeleteFile()
{
if (!isSavable)
return;
var path = m_AbsolutePath + m_FileName;
if (!File.Exists(path))
return;
File.Delete(path);
}
public void Dispose()
{
if (m_ViewModel != null)
UnityObject.DestroyImmediate(m_ViewModel);
m_Asset = null;
}
public static TViewModel CreateViewModel()
{
var model = ScriptableObject.CreateInstance<TViewModel>();
model.hideFlags |= HideFlags.HideAndDontSave;
return model;
}
TViewModel LoadViewModel()
{
if (string.IsNullOrEmpty(m_FileName))
return null;
var objects = InternalEditorUtility.LoadSerializedFileAndForget(m_RelativePath + m_FileName);
if (objects.Length <= 0 || objects[0] == null)
return null;
var model = (TViewModel)objects[0];
model.hideFlags |= HideFlags.HideAndDontSave;
return model;
}
static string GetAssetKey(UnityObject asset)
{
return asset == null ? string.Empty : AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(asset));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80ae83fdf1fb2c649bccb8c293b94556
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline
{
[Serializable]
class TrackViewModelData : ISerializationCallbackReceiver
{
public static readonly float DefaultinlineAnimationCurveHeight = 100.0f;
public bool collapsed = true;
public bool showMarkers = true;
public bool showInlineCurves = false;
public float inlineAnimationCurveHeight = DefaultinlineAnimationCurveHeight;
public int lastInlineCurveDataID = -1;
public TreeViewState inlineCurvesState = null;
public Rect inlineCurvesShownAreaInsideMargins = new Rect(1, 1, 1, 1);
public Dictionary<int, long> markerTimeStamps = new Dictionary<int, long>();
[SerializeField] List<int> m_MarkerTimeStampsKeys;
[SerializeField] List<long> m_MarkerTimeStampsValues;
public void OnBeforeSerialize()
{
if (markerTimeStamps == null)
return;
m_MarkerTimeStampsKeys = new List<int>(markerTimeStamps.Count);
m_MarkerTimeStampsValues = new List<long>(markerTimeStamps.Count);
foreach (var kvp in markerTimeStamps)
{
m_MarkerTimeStampsKeys.Add(kvp.Key);
m_MarkerTimeStampsValues.Add(kvp.Value);
}
}
public void OnAfterDeserialize()
{
markerTimeStamps = new Dictionary<int, long>();
if (m_MarkerTimeStampsKeys == null || m_MarkerTimeStampsValues == null ||
m_MarkerTimeStampsKeys.Count != m_MarkerTimeStampsValues.Count)
return;
for (int i = 0; i < m_MarkerTimeStampsKeys.Count; ++i)
markerTimeStamps.Add(m_MarkerTimeStampsKeys[i], m_MarkerTimeStampsValues[i]);
}
}
[Serializable]
class TimelineAssetViewModel : ScriptableObject, ISerializationCallbackReceiver
{
public const float DefaultTrackScale = 1.0f;
public const float DefaultVerticalScroll = 0;
public static readonly Vector2 TimeAreaDefaultRange = new Vector2(-WindowConstants.timeAreaShownRangePadding, 5.0f); // in seconds. Hack: using negative value to force the UI to have a left margin at 0.
public static readonly Vector2 NoPlayRangeSet = new Vector2(float.MaxValue, float.MaxValue);
public bool timeInFrames = true;
public Vector2 timeAreaShownRange = TimeAreaDefaultRange;
public bool showAudioWaveform = true;
public float trackScale = DefaultTrackScale;
public bool playRangeEnabled;
public Vector2 timeAreaPlayRange = NoPlayRangeSet;
public double windowTime;
public float verticalScroll = DefaultVerticalScroll;
public bool showMarkerHeader;
public Dictionary<TrackAsset, TrackViewModelData> tracksViewModelData = new Dictionary<TrackAsset, TrackViewModelData>();
// Used only for serialization of the dictionary
[SerializeField] List<TrackAsset> m_Keys = new List<TrackAsset>();
[SerializeField] List<TrackViewModelData> m_Vals = new List<TrackViewModelData>();
public void OnBeforeSerialize()
{
m_Keys.Clear();
m_Vals.Clear();
foreach (var data in tracksViewModelData)
{
// Assets that don't save, will create nulls when deserializeds
if (data.Key != null && data.Value != null && (data.Key.hideFlags & HideFlags.DontSave) == 0)
{
m_Keys.Add(data.Key);
m_Vals.Add(data.Value);
}
}
}
public void OnAfterDeserialize()
{
}
public void OnEnable()
{
if (m_Keys.Count == m_Vals.Count)
{
tracksViewModelData.Clear();
for (int i = 0; i < m_Keys.Count; i++)
{
if (m_Keys[i] != null) // if the asset is overwritten the tracks can be null
tracksViewModelData[m_Keys[i]] = m_Vals[i];
}
}
m_Keys.Clear();
m_Vals.Clear();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d79cb9ecc0d4a6d428ab98a681a33897
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,170 @@
using UnityEngine;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
using ViewModelsMap = System.Collections.Generic.Dictionary<UnityEngine.Timeline.TimelineAsset, UnityEditor.Timeline.ScriptableObjectViewPrefs<UnityEditor.Timeline.TimelineAssetViewModel>>;
using ViewModelsList = System.Collections.Generic.List<UnityEditor.Timeline.ScriptableObjectViewPrefs<UnityEditor.Timeline.TimelineAssetViewModel>>;
namespace UnityEditor.Timeline
{
static class TimelineWindowViewPrefs
{
public const string FilePath = "Library/Timeline";
static readonly ViewModelsMap k_ViewModelsMap = new ViewModelsMap();
static readonly ViewModelsList k_UnassociatedViewModels = new ViewModelsList();
public static int viewModelCount
{
get { return k_ViewModelsMap.Count + k_UnassociatedViewModels.Count; }
}
public static TimelineAssetViewModel GetOrCreateViewModel(TimelineAsset asset)
{
if (asset == null)
return CreateUnassociatedViewModel();
ScriptableObjectViewPrefs<TimelineAssetViewModel> vm;
if (k_ViewModelsMap.TryGetValue(asset, out vm))
return vm.viewModel;
return CreateViewModel(asset).viewModel;
}
public static TimelineAssetViewModel CreateUnassociatedViewModel()
{
var vm = new ScriptableObjectViewPrefs<TimelineAssetViewModel>(null, FilePath);
k_UnassociatedViewModels.Add(vm);
return vm.viewModel;
}
static ScriptableObjectViewPrefs<TimelineAssetViewModel> CreateViewModel(TimelineAsset asset)
{
var vm = new ScriptableObjectViewPrefs<TimelineAssetViewModel>(asset, FilePath);
k_ViewModelsMap.Add(asset, vm);
return vm;
}
public static void SaveViewModel(TimelineAsset asset)
{
if (asset == null)
return;
ScriptableObjectViewPrefs<TimelineAssetViewModel> vm;
if (!k_ViewModelsMap.TryGetValue(asset, out vm))
vm = CreateViewModel(asset);
vm.Save();
}
public static void SaveAll()
{
foreach (var kvp in k_ViewModelsMap)
kvp.Value.Save();
}
public static void UnloadViewModel(TimelineAsset asset)
{
ScriptableObjectViewPrefs<TimelineAssetViewModel> vm;
if (k_ViewModelsMap.TryGetValue(asset, out vm))
{
vm.Dispose();
k_ViewModelsMap.Remove(asset);
}
}
public static void UnloadAllViewModels()
{
foreach (var kvp in k_ViewModelsMap)
kvp.Value.Dispose();
foreach (var vm in k_UnassociatedViewModels)
vm.Dispose();
k_ViewModelsMap.Clear();
k_UnassociatedViewModels.Clear();
}
public static TrackViewModelData GetTrackViewModelData(TrackAsset track)
{
if (track == null)
return new TrackViewModelData();
if (track.timelineAsset == null)
return new TrackViewModelData();
var prefs = GetOrCreateViewModel(track.timelineAsset);
TrackViewModelData trackData;
if (prefs.tracksViewModelData.TryGetValue(track, out trackData))
{
return trackData;
}
trackData = new TrackViewModelData();
prefs.tracksViewModelData[track] = trackData;
return trackData;
}
public static bool IsTrackCollapsed(TrackAsset track)
{
if (track == null)
return true;
return GetTrackViewModelData(track).collapsed;
}
public static void SetTrackCollapsed(TrackAsset track, bool collapsed)
{
if (track == null)
return;
GetTrackViewModelData(track).collapsed = collapsed;
}
public static bool IsShowMarkers(TrackAsset track)
{
if (track == null)
return true;
return GetTrackViewModelData(track).showMarkers;
}
public static void SetTrackShowMarkers(TrackAsset track, bool collapsed)
{
if (track == null)
return;
GetTrackViewModelData(track).showMarkers = collapsed;
}
public static bool GetShowInlineCurves(TrackAsset track)
{
if (track == null)
return false;
return GetTrackViewModelData(track).showInlineCurves;
}
public static void SetShowInlineCurves(TrackAsset track, bool inlineOn)
{
if (track == null)
return;
GetTrackViewModelData(track).showInlineCurves = inlineOn;
}
public static float GetInlineCurveHeight(TrackAsset asset)
{
if (asset == null)
return TrackViewModelData.DefaultinlineAnimationCurveHeight;
return GetTrackViewModelData(asset).inlineAnimationCurveHeight;
}
public static void SetInlineCurveHeight(TrackAsset asset, float height)
{
if (asset != null)
GetTrackViewModelData(asset).inlineAnimationCurveHeight = height;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 105515c1653548242b4fe973c0f375f7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,45 @@
namespace UnityEditor.Timeline
{
static class WindowConstants
{
public const float timeAreaYPosition = 19.0f;
public const float timeAreaHeight = 22.0f;
public const float timeAreaMinWidth = 50.0f;
public const float timeAreaShownRangePadding = 5.0f;
public const float markerRowHeight = 18.0f;
public const float markerRowYPosition = timeAreaYPosition + timeAreaHeight;
public const float defaultHeaderWidth = 315.0f;
public const float defaultBindingAreaWidth = 40.0f;
public const float minHierarchySplitter = 0.15f;
public const float maxHierarchySplitter = 10.50f;
public const float hierarchySplitterDefaultPercentage = 0.2f;
public const float minHeaderWidth = 315.0f;
public const float maxHeaderWidth = 650.0f;
public const float maxTimeAreaScaling = 90000.0f;
public const float minTimeCodeWidth = 28.0f; // Enough space to display up to 9999 without clipping
public const float sliderWidth = 15;
public const float shadowUnderTimelineHeight = 15.0f;
public const float createButtonWidth = 70.0f;
public const float refTimeWidth = 50.0f;
public const float selectorWidth = 23.0f;
public const float cogButtonWidth = 32.0f;
public const float cogButtonPadding = 16.0f;
public const float trackHeaderButtonSize = 16.0f;
public const float trackHeaderButtonPadding = 6f;
public const float trackHeaderButtonSpacing = 3.0f;
public const float trackOptionButtonVerticalPadding = 0f;
public const float trackHeaderMaxButtonsWidth = 5 * (trackHeaderButtonSize + trackHeaderButtonPadding);
public const float trackInsertionMarkerHeight = 1f;
public const int autoPanPaddingInPixels = 50;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d768b1bb52e2c64ba818933dbdd8452
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: