Unity-jump-proj

This commit is contained in:
2024-09-09 11:07:16 +03:00
parent 2c29906bbf
commit fd96a5627d
13707 changed files with 866380 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,91 @@
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 state.ignorePreview ? TimelineModeGUIState.Disabled : TimelineModeGUIState.Enabled;
}
public virtual TimelineModeGUIState EditModeButtonsState(WindowState state)
{
return TimelineModeGUIState.Enabled;
}
}
/// <summary>
/// Different mode for Timeline
/// </summary>
[Flags]
public enum TimelineModes
{
/// <summary>
/// A playable director with a valid timeline is selected in editor.
/// </summary>
Active = 1,
/// <summary>
/// The timeline is not editable. (the TimelineAsset file is either readonly on disk or locked by source control).
/// </summary>
ReadOnly = 2,
/// <summary>
/// The timeline cannot be played or previewed.
/// </summary>
Inactive = 4,
/// <summary>
/// Disabled Timeline.
/// </summary>
Disabled = 8,
/// <summary>
/// Timeline in AssetEditing mode.
/// This mode is enabled when a timeline asset is selected in the project window.
/// </summary>
AssetEdition = 16,
/// <summary>
/// The timeline can be edited (either through playable director or selected timeline asset in project window).
/// </summary>
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,94 @@
using System;
using UnityEngine;
namespace UnityEditor.Timeline
{
readonly struct OverlayDrawer
{
enum OverlayType
{
BackgroundColor,
BackgroundTexture,
TextBox
}
readonly OverlayType m_Type;
readonly Rect m_Rect;
readonly string m_Text;
readonly Texture2D m_Texture;
readonly Color m_Color;
readonly GUIStyle m_BackgroundTextStyle;
readonly GUIStyle m_TextStyle;
OverlayDrawer(Rect rectangle, Color backgroundColor)
{
m_Type = OverlayType.BackgroundColor;
m_Rect = rectangle;
m_Color = backgroundColor;
m_Text = string.Empty;
m_Texture = null;
m_BackgroundTextStyle = null;
m_TextStyle = null;
}
OverlayDrawer(Rect rectangle, Texture2D backTexture)
{
m_Type = OverlayType.BackgroundTexture;
m_Rect = rectangle;
m_Color = Color.clear;
m_Text = string.Empty;
m_Texture = backTexture;
m_BackgroundTextStyle = null;
m_TextStyle = null;
}
OverlayDrawer(Rect rectangle, string msg, GUIStyle textStyle, Color textColor, Color bgTextColor, GUIStyle bgTextStyle)
{
m_Type = OverlayType.TextBox;
m_Rect = rectangle;
m_Text = msg;
m_TextStyle = textStyle;
m_TextStyle.normal.textColor = textColor;
m_BackgroundTextStyle = bgTextStyle;
m_BackgroundTextStyle.normal.textColor = bgTextColor;
m_Texture = null;
m_Color = Color.clear;
}
public static OverlayDrawer CreateColorOverlay(Rect rectangle, Color backgroundColor)
{
return new OverlayDrawer(rectangle, backgroundColor);
}
public static OverlayDrawer CreateTextureOverlay(Rect rectangle, Texture2D backTexture)
{
return new OverlayDrawer(rectangle, backTexture);
}
public static OverlayDrawer CreateTextBoxOverlay(Rect rectangle, string msg, GUIStyle textStyle, Color textColor, Color bgTextColor, GUIStyle bgTextStyle)
{
return new OverlayDrawer(rectangle, msg, textStyle, textColor, bgTextColor, bgTextStyle);
}
public void Draw()
{
Rect overlayRect = GUIClip.Clip(m_Rect);
switch (m_Type)
{
case OverlayType.BackgroundColor:
EditorGUI.DrawRect(overlayRect, m_Color);
break;
case OverlayType.BackgroundTexture:
Graphics.DrawTextureRepeated(overlayRect, m_Texture);
break;
case OverlayType.TextBox:
{
using (new GUIColorOverride(m_BackgroundTextStyle.normal.textColor))
GUI.Box(overlayRect, GUIContent.none, m_BackgroundTextStyle);
Graphics.ShadowLabel(overlayRect, GUIContent.Temp(m_Text), m_TextStyle, m_TextStyle.normal.textColor, Color.black);
break;
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f4398a10f384dacb37698e5b1f8fede
timeCreated: 1597264928

View File

@ -0,0 +1,67 @@
using UnityEngine;
namespace UnityEditor.Timeline
{
/// <summary>
/// Scrolling mode during playback for the timeline window.
/// </summary>
public enum PlaybackScrollMode
{
/// <summary>
/// Timeline window doesn't change while the playhead is leaving the window.
/// </summary>
None,
/// <summary>
/// Timeline window pans its content when the playhead arrive at the right of the window (like a paging scrolling).
/// </summary>
Pan,
/// <summary>
/// Timeline window move the content as the playhead moves.
/// When the playhead reach the middle of the window, it stays there and the content scroll behind it.
/// </summary>
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,108 @@
using System;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
/// <summary>
/// A context for the Timeline window (RO)
/// </summary>
/// <remarks>
/// The SequenceContext represents a state of the Timeline window, and is used to interact with <see cref="TimelineNavigator"/>.
/// </remarks>
public readonly struct SequenceContext : IEquatable<SequenceContext>
{
/// <summary>
/// The director associated with the Timeline window in the context. (RO)
/// </summary>
public PlayableDirector director { get; }
/// <summary>
/// The <see cref="TimelineClip"/> associated with the Timeline window in the context. (RO)
/// </summary>
/// <remarks>In a SubTimeline context, the clip is the <see cref="TimelineClip"/> that hosts the SubTimeline in the parent Timeline.
/// In the root context, the clip is <see langword="null"/>.</remarks>
public TimelineClip clip { get; }
/// <summary>
/// Initializes and returns an instance of SequenceContext.
/// </summary>
/// <param name="director">The PlayableDirector associated with the context. Must be a valid PlayableDirector reference. </param>
/// <param name="clip">The TimelineClip reference that controls the sequence. Specify <see langword="null"/> to specify that the sequence is the root. If non-null, the clip must be part of a valid <see cref="TimelineAsset"/>.</param>
/// <exception cref="System.ArgumentNullException"> <paramref name="director"/> is null.</exception>
/// <exception cref="System.ArgumentException"> The <paramref name="clip"/> is not part of a <see cref="TrackAsset"/>.</exception>
/// <exception cref="System.ArgumentException"> The <paramref name="clip"/> is part of a track but not part of a <see cref="TimelineAsset"/>.</exception>
public SequenceContext(PlayableDirector director, TimelineClip clip)
{
if (director == null)
throw new ArgumentNullException(nameof(director));
var parentTrack = clip?.GetParentTrack();
if (clip != null && parentTrack == null)
throw new ArgumentException("The provided clip must be part of a track", nameof(clip));
if (clip != null && parentTrack.timelineAsset == null)
throw new ArgumentException("The provided clip must be part of a Timeline.", nameof(clip));
this.director = director;
this.clip = clip;
m_Valid = true;
}
/// <summary>
/// Assesses the validity of a SequenceContext.
/// </summary>
/// <remarks>To be valid, a SequenceContext must contain a valid PlayableDirector reference.</remarks>
/// <returns><see langword="true" /> if the SequenceContext is valid,<see langword="false" /> otherwise</returns>
public bool IsValid() => m_Valid;
/// <summary>
/// Equality operator overload.
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns><see langword="true" /> if operands are equal, <see langword="false" /> otherwise.</returns>
public static bool operator ==(SequenceContext left, SequenceContext right) => left.Equals(right);
/// <summary>
/// Inequality operator overload.
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns><see langword="true" /> if operands are not equal, <see langword="false" /> otherwise.</returns>
public static bool operator !=(SequenceContext left, SequenceContext right) => !left.Equals(right);
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
/// <param name="other">An object to compare with this object.</param>
/// <returns>
/// <see langword="true" /> if the current object is equal to the <paramref name="other" /> parameter; otherwise, <see langword="false" />.</returns>
public bool Equals(SequenceContext other)
{
return Equals(director, other.director) && Equals(clip, other.clip);
}
/// <summary>Indicates whether the current object is equal to another object of indeterminate type.</summary>
/// <param name="obj">An object to compare with this object.</param>
/// <returns>
/// <see langword="true" /> if the current object is equal to the <paramref name="obj" /> parameter; otherwise, <see langword="false" />.</returns>
public override bool Equals(object obj)
{
return obj is SequenceContext other && Equals(other);
}
/// <summary>Hash function for SequenceContext.</summary>
/// <returns>
/// Hash code for the SequenceContext.
/// </returns>
public override int GetHashCode()
{
unchecked
{
return ((director != null ? director.GetHashCode() : 0) * 397) ^ (clip != null ? clip.GetHashCode() : 0);
}
}
internal static SequenceContext Invalid = new SequenceContext();
readonly bool m_Valid;
}
}

View File

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

View File

@ -0,0 +1,44 @@
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
/// <summary>
/// Base class of the TimelineWindow.
/// </summary>
public abstract class TimelineEditorWindow : EditorWindow
{
/// <summary>
/// Use this interface to navigate between Timelines and Sub-Timelines. (RO)
/// </summary>
public abstract TimelineNavigator navigator { get; }
/// <summary>
/// Use this interface to control the playback behaviour of the Timeline window. (RO)
/// </summary>
public abstract TimelinePlaybackControls playbackControls { get; }
/// <summary>
/// Retrieves and sets the Timeline Window lock state. When disabled (false), the window focus follows the Unity selection.
/// </summary>
/// <remarks>When the lock state transitions from true to false, the focused timeline is synchronized with the Unity selection.</remarks>>
public abstract bool locked { get; set; }
/// <summary>
/// Sets which TimelineAsset is shown in the TimelineWindow.
/// </summary>
/// <param name="sequence">The TimelineAsset to show. Specify a null to clear the TimelineWindow.</param>
/// <remarks>When you call this method, the TimelineWindow is placed in asset edit mode. This mode does not support all features. For example, bindings are not available and the timeline cannot be evaluated.
/// You can use this method when the TimelineWindow is locked.</remarks>
public abstract void SetTimeline(TimelineAsset sequence);
/// <summary>
/// Sets which TimelineAsset is shown in the TimelineWindow based on the PlayableDirector.
/// </summary>
/// <param name="director">The PlayableDirector associated with the TimelineAsset to show in the TimelineWindow. Specify a null to clear the TimelineWindow.</param>
/// <remarks>You can use this method when the TimelineWindow is locked.</remarks>
public abstract void SetTimeline(PlayableDirector director);
/// <summary>
/// Clears the TimelineAsset that is shown in the TimelineWindow.
/// </summary>
/// <remarks>You can use this method when the TimelineWindow is locked.</remarks>>
public abstract void ClearTimeline();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dfe0d35963084dcc899e4d32cca85455
timeCreated: 1590671897

View File

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Timeline.Actions;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
class TimelineMarkerHeaderGUI : IRowGUI, ILayerable
{
static readonly GUIContent k_Muted = L10n.TextContent("Muted");
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 trackHeaderFont;
public Color colorTrackFont;
public bool isMuted;
public bool isSelected;
}
public TimelineMarkerHeaderGUI(TimelineAsset asset, WindowState state)
{
m_TrackHash = -1;
timeline = asset;
this.state = state;
}
public TrackAsset asset => timeline.markerTrack;
public Rect boundingRect { get; private set; }
public bool showMarkers => state.showMarkerHeader;
public bool muted => timeline.markerTrack != null && timeline.markerTrack.muted;
public bool locked => timeline.markerTrack.locked;
public LayerZOrder zOrder => m_ZOrder;
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,
trackHeaderFont = DirectorStyles.Instance.trackHeaderFont,
colorTrackFont = DirectorStyles.Instance.customSkin.colorTrackFont,
isMuted = muted,
isSelected = IsSelected()
};
if (state.showMarkerHeader)
{
DrawMarkerDrawer(data);
if (Event.current.type == EventType.Repaint)
state.spacePartitioner.AddBounds(this, boundingRect);
}
if (asset != null && Hash() != m_TrackHash)
Rebuild();
Rect rect = state.showMarkerHeader ? markerContentRect : state.timeAreaRect;
using (new GUIViewportScope(rect))
{
if (m_Layer != null)
m_Layer.Draw(rect, state);
HandleDragAndDrop();
}
if (state.showMarkerHeader && data.isMuted)
DrawMuteOverlay(data);
}
public void Rebuild()
{
if (asset == null)
return;
m_Layer = new MarkersLayer(Layer.MarkersOnHeader, this);
m_TrackHash = Hash();
}
void HandleDragAndDrop()
{
if (state.editSequence.isReadOnly || !state.showMarkerHeader)
return;
if (Event.current == null || Event.current.type != EventType.DragUpdated &&
Event.current.type != EventType.DragPerform && Event.current.type != EventType.DragExited)
return;
var objectsBeingDropped = DragAndDrop.objectReferences.OfType<Object>();
var candidateTime = TimelineHelpers.GetCandidateTime(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, ResolveType);
if (perform && DragAndDrop.visualMode == DragAndDropVisualMode.Copy)
{
DragAndDrop.AcceptDrag();
}
}
static bool ResolveType(IEnumerable<Type> types, Action<Type> onComplete, string formatString)
{
void CreateMarkerTrackOnComplete(Type type)
{
WindowState state = TimelineWindow.instance.state;
state.editSequence.asset.CreateMarkerTrack();
state.showMarkerHeader = true;
onComplete(type);
}
return TimelineDragging.ResolveType(types, CreateMarkerTrackOnComplete, formatString);
}
int Hash()
{
return timeline.markerTrack == null ? 0 : timeline.markerTrack.Hash();
}
static void DrawMarkerDrawer(DrawData data)
{
DrawMarkerDrawerHeaderBackground(data);
DrawMarkerDrawerHeader(data);
DrawMarkerDrawerContentBackground(data);
}
static void DrawMarkerDrawerHeaderBackground(DrawData data)
{
Color backgroundColor = data.isSelected
? DirectorStyles.Instance.customSkin.colorSelection
: DirectorStyles.Instance.customSkin.markerHeaderDrawerBackgroundColor;
EditorGUI.DrawRect(data.headerRect, backgroundColor);
}
static void DrawMarkerDrawerHeader(DrawData data)
{
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);
buttonRect.x -= 21.0f;
DrawMuteButton(buttonRect, data);
}
static void DrawMarkerDrawerContentBackground(DrawData data)
{
Color trackBackgroundColor = DirectorStyles.Instance.customSkin.markerDrawerBackgroundColor;
if (data.isSelected)
trackBackgroundColor = DirectorStyles.Instance.customSkin.colorTrackBackgroundSelected;
EditorGUI.DrawRect(data.contentRect, trackBackgroundColor);
}
static void DrawMuteOverlay(DrawData data)
{
DirectorStyles styles = TimelineWindow.styles;
var colorOverlay = OverlayDrawer.CreateColorOverlay(GUIClip.Unclip(data.contentRect), styles.customSkin.colorTrackDarken);
colorOverlay.Draw();
Rect textRect = Graphics.CalculateTextBoxSize(data.contentRect, styles.fontClip, k_Muted, WindowConstants.overlayTextPadding);
var boxOverlay = OverlayDrawer.CreateTextBoxOverlay(
GUIClip.Unclip(textRect),
k_Muted.text,
styles.fontClip,
Color.white,
styles.customSkin.colorLockTextBG,
styles.displayBackground);
boxOverlay.Draw();
}
static void DrawTrackDropDownMenu(Rect rect)
{
if (GUI.Button(rect, GUIContent.none, DirectorStyles.Instance.trackOptions))
{
SelectionManager.SelectOnly(TimelineEditor.inspectedAsset.markerTrack);
SequencerContextMenu.ShowTrackContextMenu(null);
}
}
static void DrawMuteButton(Rect rect, DrawData data)
{
bool muted = GUI.Toggle(rect, data.isMuted, string.Empty, TimelineWindow.styles.trackMuteButton);
if (muted != data.isMuted)
new[] { TimelineEditor.inspectedAsset.markerTrack }.Invoke<MuteTrack>();
}
bool IsSelected()
{
return SelectionManager.Contains(asset);
}
}
}

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,97 @@
using System.Collections.Generic;
namespace UnityEditor.Timeline
{
/// <summary>
/// Interface to navigate through Timelines and SubTimelines for the Timeline window.
/// </summary>
/// <remarks>
/// TimelineNavigator gives you access to the Timeline window breadcrumbs functionality. Use it to programmatically
/// dig into SubTimelines, navigate to parent Timelines or navigate Timeline Window breadcrumbs.
/// </remarks>
public sealed class TimelineNavigator
{
TimelineWindow.TimelineNavigatorImpl m_Impl;
internal TimelineNavigator(IWindowStateProvider windowState)
{
m_Impl = new TimelineWindow.TimelineNavigatorImpl(windowState);
}
/// <summary>
/// Gets the SequenceContext associated with the Timeline currently shown in the Timeline window.
/// </summary>
/// <returns>The SequenceContext associated with the Timeline currently shown in the Timeline window.</returns>
/// <remarks>Equivalent to <c>TimelineNavigator.GetBreadCrumbs().Last()</c></remarks>
/// <exception cref="System.InvalidOperationException"> The Window associated to this instance has been destroyed.</exception>
public SequenceContext GetCurrentContext()
{
return m_Impl.GetCurrentContext();
}
/// <summary>
/// Gets the parent SequenceContext for the Timeline currently shown in the Timeline window.
/// </summary>
/// <returns>The parent SequenceContext for the Timeline currently shown in the Timeline window if there is one; an invalid SequenceContext otherwise. <seealso cref="SequenceContext.Invalid"/></returns>
/// <exception cref="System.InvalidOperationException"> The Window associated to this instance has been destroyed.</exception>
public SequenceContext GetParentContext()
{
return m_Impl.GetParentContext();
}
/// <summary>
/// Gets the first SequenceContext in the breadcrumbs.
/// </summary>
/// <returns>The first SequenceContext in the breadcrumbs.</returns>
/// <remarks>Equivalent to <c>TimelineNavigator.GetBreadCrumbs().First()</c></remarks>
/// <exception cref="System.InvalidOperationException"> The Window associated to this instance has been destroyed.</exception>
public SequenceContext GetRootContext()
{
return m_Impl.GetRootContext();
}
/// <summary>
/// Gets the collection of child contexts that can be navigated to from the current context.
/// </summary>
/// <returns>The collection of child contexts that can be navigated to from the current context.</returns>
/// <exception cref="System.InvalidOperationException"> The Window associated to this instance has been destroyed.</exception>
public IEnumerable<SequenceContext> GetChildContexts()
{
return m_Impl.GetChildContexts();
}
/// <summary>
/// Gets the collection of SequenceContexts associated with the breadcrumbs shown in the TimelineEditorWindow.
/// </summary>
/// <remarks>This operation can be expensive. Consider caching the results instead of calling the method multiple times.</remarks>
/// <returns>The collection of SequenceContexts associated with the breadcrumbs shown in the TimelineEditorWindow, from the root context to the current context.</returns>
/// <exception cref="System.InvalidOperationException"> The Window associated to this instance has been destroyed.</exception>
public IEnumerable<SequenceContext> GetBreadcrumbs()
{
return m_Impl.GetBreadcrumbs();
}
/// <summary>
/// Navigates to a new SequenceContext.
/// </summary>
/// <param name="context">The context to navigate to.</param>
/// <remarks>
/// The SequenceContext provided must be a valid navigation destination.
///
/// Valid navigation destinations:
/// * The parent context returned by <see cref="GetParentContext"/>.
/// * The root context returned by <see cref="GetRootContext"/>.
/// * Any SequenceContext returned by <see cref="GetChildContexts"/>.
/// * Any SequenceContext returned by <see cref="GetBreadcrumbs"/>.
///
/// Note: This method cannot be used to change the root SequenceContext. To change the root SequenceContext, use <see cref="TimelineEditorWindow.SetTimeline"/>.
///
/// </remarks>
/// <exception cref="System.InvalidOperationException"> The Window associated to this instance has been destroyed.</exception>
/// <exception cref="System.ArgumentException"> The context is not valid.</exception>
/// <exception cref="System.InvalidOperationException"> The context is not a valid navigation destination.</exception>
public void NavigateTo(SequenceContext context)
{
m_Impl.NavigateTo(context);
}
}
}

View File

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

View File

@ -0,0 +1,144 @@
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Timeline
{
internal interface ITimelinePlaybackControls
{
void Play();
void Pause();
void PreviousFrame();
void NextFrame();
void GoToFirstFrame();
void GoToLastFrame();
void SetCurrentTime(double time, TimelinePlaybackControls.Context context);
void SetCurrentFrame(int frame, TimelinePlaybackControls.Context context);
double GetCurrentTime(TimelinePlaybackControls.Context context);
int GetCurrentFrame(TimelinePlaybackControls.Context context);
}
/// <summary>
/// Use the TimelinePlaybackControls to manage the Timeline window's playback state, playhead location, and play range.
/// </summary>
public sealed class TimelinePlaybackControls
{
TimelineWindow.TimelinePlaybackControlsImpl m_Impl;
internal TimelinePlaybackControls(IWindowStateProvider stateProvider)
{
m_Impl = new TimelineWindow.TimelinePlaybackControlsImpl(stateProvider);
}
/// <summary>
/// Use Context to specify whether the time is based on local time or global time.
/// </summary>
public enum Context
{
/// <summary>
/// Time is relative to the current Timeline
/// </summary>
Local,
/// <summary>
/// Time is relative to the main Timeline
/// </summary>
Global
}
/// <summary>
/// Starts playback.
/// </summary>
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
public void Play() { m_Impl.Play(); }
/// <summary>
/// Pauses playback.
/// </summary>
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
public void Pause() { m_Impl.Pause(); }
/// <summary>
/// Moves the playhead to the previous frame.
/// </summary>
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
public void PreviousFrame() { m_Impl.PreviousFrame(); }
/// <summary>
/// Moves the playhead to the next frame.
/// </summary>
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
public void NextFrame() { m_Impl.NextFrame(); }
/// <summary>
/// Moves the playhead to the first frame.
/// </summary>
/// <exception cref="System.InvalidOperationException"> The Window associated with this instance has been destroyed.</exception>
public void GoToFirstFrame() { m_Impl.GoToFirstFrame(); }
/// <summary>
/// Moves the playhead to the last frame.
/// </summary>
/// <exception cref="System.InvalidOperationException"> The Window associated with this instance has been destroyed.</exception>
public void GoToLastFrame() { m_Impl.GoToLastFrame(); }
/// <summary>
/// Moves the playhead to a specific time.
/// </summary>
/// <param name="time">The time in seconds.</param>
/// <param name="context">
/// Use Context with a Sub-Timeline to specify whether the specified time is relative to the Sub-Timeline or the main Timeline.
/// If the Timeline is not a Sub-Timeline, the context uses local time regardless of the specified context.
/// </param>
/// Use <see cref="Context.Local"/>, the default, to move the playhead relative to the Sub-Timeline or Timeline.
/// Use <see cref="Context.Global"/> to move the playhead relative to the main Timeline.
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
/// <exception cref="System.ArgumentException">The context is invalid.</exception>
public void SetCurrentTime(double time, Context context = Context.Local) { m_Impl.SetCurrentTime(time, context); }
/// <summary>
/// Moves the playhead to a specific frame.
/// </summary>
/// <param name="frame">The frame to move to.</param>
/// <param name="context">
/// Use Context with a Sub-Timeline to specify whether the specified frame is relative to the Sub-Timeline or the main Timeline.
/// If the Timeline is not a Sub-Timeline, the context uses local time regardless of the specified context.
/// </param>
/// Use <see cref="Context.Local"/>, the default, to move the playhead relative to the Sub-Timeine.
/// Use <see cref="Context.Global"/> to move the playhead relative to the main Timeline.
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
/// <exception cref="System.ArgumentException">The context is invalid.</exception>
public void SetCurrentFrame(int frame, Context context = Context.Local) { m_Impl.SetCurrentFrame(frame, context); }
/// <summary>
/// Retrieves the location of the timeline playhead in seconds.
/// </summary>
/// <param name="context">
/// Use Context with a Sub-Timeline to specify whether the returned value is relative to the Sub-Timeline or the main Timeline.
/// If the Timeline is not a Sub-Timeline, the context uses local time regardless of the specified context.
/// </param>
/// Use <see cref="Context.Local"/>, the default, to retrieve the playhead location relative to the Sub-Timeline.
/// Use <see cref="Context.Global"/> to retrive the location relative to the main Timeline.
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
/// <exception cref="System.ArgumentException">The context is invalid.</exception>
/// <returns>The playhead location in seconds.</returns>
public double GetCurrentTime(Context context = Context.Local)
{
return m_Impl.GetCurrentTime(context);
}
/// <summary>
/// Retrieves the location of the timeline playhead in frames.
/// </summary>
/// <param name="context">
/// Use Context with a Sub-Timeline to specify whether the returned value is relative to the Sub-Timeline or the main Timeline.
/// If the Timeline is not a Sub-Timeline, the context uses local time regardless of the specified context.
/// </param>
/// Use <see cref="Context.Local"/>, the default, to retrieve the playhead location relative to the Sub-Timeline.
/// Use <see cref="Context.Global"/> to retrive the playhead location relative to the main Timeline.
/// <exception cref="System.InvalidOperationException">The Window associated with this instance has been destroyed.</exception>
/// <exception cref="System.ArgumentException">The context is invalid.</exception>
/// <returns>The playhead location in frames.</returns>
public int GetCurrentFrame(Context context = Context.Local)
{
return m_Impl.GetCurrentFrame(context);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b662f155e14546d0b72f129800d013d0
timeCreated: 1620849473

View File

@ -0,0 +1,560 @@
using System;
using System.Collections.Generic;
using UnityEditor.Callbacks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline
{
internal interface IWindowStateProvider
{
IWindowState windowState { get; }
}
[EditorWindowTitle(title = "Timeline", useTypeNameAsIconName = true)]
partial class TimelineWindow : TimelineEditorWindow, IHasCustomMenu, IWindowStateProvider
{
[Serializable]
public class TimelineWindowPreferences
{
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; }
IWindowState IWindowStateProvider.windowState => state;
public override 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();
UpdateTitle();
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 OnHierarchyChange()
{
hierarchyChangedThisFrame = true;
Repaint();
}
void OnStateChange()
{
state.UpdateRecordingState();
state.editSequence.InvalidateChildAssetCache();
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);
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 (state != null && state.ignorePreview && state.playing)
{
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)
{
var fillColor = new Color(1.0f, 1.0f, 1.0f, 0.1f);
state.spacePartitioner.DebugDraw(fillColor, Color.yellow);
state.headerSpacePartitioner.DebugDraw(fillColor, Color.green);
}
// 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;
}
if (Event.current.type == EventType.Layout)
{
UpdateTitle();
}
}
void UpdateTitle()
{
#if UNITY_2020_2_OR_NEWER
bool dirty = false;
List<Object> children = state?.editSequence.cachedChildAssets;
if (children != null)
{
foreach (var child in children)
{
dirty = EditorUtility.IsDirty(child);
if (dirty)
{
break;
}
}
}
hasUnsavedChanges = dirty;
#endif
}
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)
ClearTimeline();
m_CurrentSceneHashCode = SceneManager.GetActiveScene().GetHashCode();
}
}
}
void DetectStateChanges()
{
if (state != null)
{
foreach (var sequenceState in state.allSequences)
{
sequenceState.ResetIsReadOnly();
}
// detect if the sequence was removed under our feet
if (m_LastFrameHadSequence && state.editSequence.asset == null)
{
ClearTimeline();
}
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;
if (locked)
{
//revert lock if the original context was not asset mode
if (!state.masterSequence.isAssetOnly)
locked = false;
}
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)
{
SetTimeline(selectedDirector);
}
else
{
state.masterSequence.isAssetOnly = true;
}
}
}
else
{
// the user may have changed the timeline associated with the current director
if (state.editSequence.asset != state.editSequence.director.playableAsset)
{
if (!locked)
{
SetTimeline(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
SetTimeline(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 || currentMode.mode != TimelineModes.Active || 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 (!state.ignorePreview)
{
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 = TimelineUtility.CreateAndSaveTimelineAsset(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.SetTimeline(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.timelineLockButton, disabled);
}
}
}

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,145 @@
//#define ANALITICS_DEBUG
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
class TimelineWindowAnalytics
{
const string vendorKey = "unity.timeline";
const string eventName = "timeline_editor_info";
const int version = 2;
const int maxEventsPerHour = 1000;
const int maxNumberOfElements = 1000;
[Serializable]
internal struct timeline_asset_stats
{
public string asset_guid;
public double duration;
public double frame_rate;
public List<track_asset_stats> track_stats;
public double mix_samples_count, ripple_samples_count, replace_samples_count;
public string display_format;
}
[Serializable]
internal struct track_asset_stats
{
public string track_type;
public int clip_count;
public int marker_count;
}
class WindowAnalyticsStats
{
internal int[] editModeSamples = new int[3]; // EditModes
}
static WindowAnalyticsStats analyticsStats = new WindowAnalyticsStats();
public void SendPlayEvent(bool start)
{
if (!start || !EditorAnalytics.enabled)
{
return;
}
EditorAnalytics.RegisterEventWithLimit(eventName, maxEventsPerHour, maxNumberOfElements, vendorKey, version);
var ret = GenerateTimelineAssetStats(out var data);
if (!ret)
{
return;
}
#if ANALITICS_DEBUG
Debug.Log(JsonUtility.ToJson(data, true));
#endif
EditorAnalytics.SendEventWithLimit(eventName, data, version);
SendAfterSequenceChangeEvent();
}
public void SendAfterSequenceChangeEvent()
{
analyticsStats = new WindowAnalyticsStats(); // Wipe Window Stats
}
public void SendManipulationEndedEvent()
{
analyticsStats.editModeSamples[(int)EditMode.editType]++;
}
internal static bool GenerateTimelineAssetStats(out timeline_asset_stats data)
{
var timeline = TimelineEditor.inspectedAsset;
if (timeline == null ||
!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(timeline, out var guid, out long _))
{
data = new timeline_asset_stats();
return false;
}
data = new timeline_asset_stats
{
asset_guid = guid,
duration = timeline.duration,
frame_rate = timeline.editorSettings.frameRate,
track_stats = GetTrackAssetStats(timeline),
display_format = TimelinePreferences.instance.timeFormat.ConvertToString(),
mix_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Mix],
ripple_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Ripple],
replace_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Replace],
};
return true;
}
static List<track_asset_stats> GetTrackAssetStats(TimelineAsset timeline)
{
var ret = new List<track_asset_stats>();
foreach (var track in timeline.flattenedTracks)
{
ret.Add(new track_asset_stats
{
track_type = track.GetType().FullName,
clip_count = track.GetClips().Count(),
marker_count = track.GetMarkers().Count()
}
);
}
return ret;
}
}
static class ConversionUtilities
{
internal static string ConvertToString<T>(this T e) where T : Enum
{
return Enum.GetName(typeof(T), e).ToSnakeCase();
}
static string ToSnakeCase(this string str)
{
var sb = new StringBuilder();
for (var i = 0; i < str.Length - 1; ++i)
{
var ch = str[i];
var nCh = str[i + 1];
if (char.IsUpper(ch) && char.IsLower(nCh))
{
sb.Append("_");
}
sb.Append(ch.ToString().ToLower());
}
sb.Append(str[str.Length - 1].ToString().ToLower());
return sb.ToString().TrimStart('_');
}
}
}

View File

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

View File

@ -0,0 +1,320 @@
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;
}
#if UNITY_2021_2_OR_NEWER
const AnimationWindowState.SnapMode k_SnapMode = AnimationWindowState.SnapMode.SnapToFrame;
#else
const AnimationWindowState.SnapMode k_SnapMode = AnimationWindowState.SnapMode.SnapToClipFrame;
#endif
[SerializeField] ClipData m_ClipData;
[SerializeField] TimelineClip m_Clip;
[SerializeField] AnimationWindowState m_AnimWindowState;
TrackAsset track
{
get
{
if (m_Clip != null)
{
return m_Clip.GetParentTrack();
}
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), (float)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, k_SnapMode));
}
public override void GoToNextKeyframe()
{
var newTime = AnimationWindowUtility.GetNextKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate);
GoToTime(m_AnimWindowState.SnapToFrame(newTime, k_SnapMode));
}
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 (track != null && state != null && !state.ignorePreview)
{
state.ArmForRecord(track);
return state.recording;
}
return false;
}
public override void StopRecording()
{
if (track != null && state != null && !state.ignorePreview)
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 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,87 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
private TimelineAsset m_PreviousMasterSequence;
public override void ClearTimeline()
{
SetCurrentTimeline(null, null, null, true);
}
public override void SetTimeline(TimelineAsset seq)
{
SetCurrentTimeline(seq, null, null);
}
public override void SetTimeline(PlayableDirector director)
{
SetCurrentTimeline(director, null);
}
public void SetCurrentTimeline(PlayableDirector director, TimelineClip hostClip)
{
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();
state.analytics.SendAfterSequenceChangeEvent(); // Changing timelines resets analytics that are aggregated in the Timeline Window
}
}
}
}

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,124 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_2021_2_OR_NEWER
using UnityEditor.SceneManagement;
#else
using UnityEditor.Experimental.SceneManagement;
#endif
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 = 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 DrawBreadcrumbs()
{
if (state == null)
return;
var count = 0;
foreach (var sequence in state.GetAllSequences())
{
var 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))
{
var width = position.width - WindowConstants.playControlsWidth - WindowConstants.cogButtonWidth;
BreadcrumbDrawer.Draw(width, m_BreadCrumbLabels, NavigateToBreadcrumbIndex);
}
}
void NavigateToBreadcrumbIndex(int index)
{
state.PopSequencesUntilCount(index + 1);
}
void DrawSequenceSelector()
{
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.
SetTimeline(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 = L10n.Tr("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(sequenceContentRect, 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)
{
UndoExtensions.RegisterTimeline(state.editSequence.asset, L10n.Tr("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,348 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEditor.ShortcutManagement;
using UnityEditor.Timeline.Actions;
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;
EditorApplication.pauseStateChanged += OnPlayModePause;
EditorApplication.globalEventHandler += GlobalEventHandler;
#if TIMELINE_FRAMEACCURATE
TimelinePlayable.playableLooped += OnPlayableLooped;
#endif
}
// This callback is needed because the Animation window registers "Animation/Key Selected" as a global hotkey
// and we want to also react to the key.
void GlobalEventHandler()
{
if (instance == null || !state.previewMode)
{
return;
}
var keyBinding = ShortcutManager.instance.GetShortcutBinding("Animation/Key Selected");
if (keyBinding.Equals(ShortcutBinding.empty))
{
return;
}
var evtCombo = KeyCombination.FromKeyboardInput(Event.current);
if (keyBinding.keyCombinationSequence.Contains(evtCombo))
{
Invoker.InvokeWithSelected<KeyAllAnimated>();
}
}
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;
EditorApplication.pauseStateChanged -= OnPlayModePause;
EditorApplication.globalEventHandler -= GlobalEventHandler;
#if TIMELINE_FRAMEACCURATE
TimelinePlayable.playableLooped -= OnPlayableLooped;
#endif
}
void OnPlayModePause(PauseState state)
{
// in PlayMode, if the timeline is playing, a constant repaint cycle occurs. Pausing the editor
// breaks the cycle, so this will restart it
Repaint();
}
// 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)
return;
//Force refresh of curve when modified by another editor.
Repaint();
if (state.previewMode == false)
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);
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.MarkDirtyTrackAndClips();
}
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];
if (mod.currentValue != null && mod.currentValue.target is IMarker currentMarker)
{
if (currentMarker.parent != null && currentMarker.parent.timelineAsset == state.editSequence.asset)
{
if (mod.currentValue.target is INotification)
TimelineEditor.Refresh(RefreshReason.ContentsModified);
else
TimelineEditor.Refresh(RefreshReason.WindowNeedsRedraw);
}
}
else if (mod.previousValue != null && mod.previousValue.target is AvatarMask) // check if an Avatar Mask has been modified
{
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.GetParentTrack();
if (selectedClip.GetParentTrack() != 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();
}
}
}
#if TIMELINE_FRAMEACCURATE
void OnPlayableLooped(Playable timelinePlayable)
{
if (state == null || !state.playing || state.masterSequence == null || state.masterSequence.director == null
|| !state.masterSequence.director.playableGraph.IsValid())
return;
var masterPlayable = state.masterSequence.director.playableGraph.GetRootPlayable(0);
if (!masterPlayable.Equals(Playable.Null)
&& masterPlayable.Equals(timelinePlayable)
&& timelinePlayable.GetGraph().IsMatchFrameRateEnabled())
timelinePlayable.SetTime(0);
}
#endif
}
}

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

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,280 @@
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
static readonly GUIContent[] k_TimeReferenceGUIContents =
{
L10n.TextContent("Local", "Display time based on the current timeline."),
L10n.TextContent("Global", "Display time based on the master timeline.")
};
TimelineMarkerHeaderGUI m_MarkerHeaderGUI;
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 DrawTransportToolbar()
{
using (new EditorGUI.DisabledScope(currentMode.PreviewState(state) == TimelineModeGUIState.Disabled))
{
PreviewModeButtonGUI();
}
using (new EditorGUI.DisabledScope(currentMode.ToolbarState(state) == TimelineModeGUIState.Disabled))
{
GotoBeginingSequenceGUI();
PreviousEventButtonGUI();
PlayButtonGUI();
NextEventButtonGUI();
GotoEndSequenceGUI();
PlayRangeButtonGUI();
TimeCodeGUI();
ReferenceTimeGUI();
}
}
void PreviewModeButtonGUI()
{
if (state.ignorePreview && !Application.isPlaying)
{
GUILayout.Label(DirectorStyles.previewDisabledContent, DirectorStyles.Instance.previewButtonDisabled);
return;
}
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(state.ignorePreview || 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)
{
TimelineAsset asset = state.editSequence.asset;
if (state.showMarkerHeader == newValue || asset == null)
return;
string undoOperation = L10n.Tr("Toggle Show Markers");
if (newValue)
{
//Create the marker track if it does not exist
TimelineUndo.PushUndo(asset, undoOperation);
asset.CreateMarkerTrack();
}
else
{
SelectionManager.Remove(asset.markerTrack);
}
asset.markerTrack.SetShowTrackMarkers(newValue);
}
static void EditModeToolbarGUI(TimelineMode mode)
{
using (new EditorGUI.DisabledScope(mode.EditModeButtonsState(instance.state) == TimelineModeGUIState.Disabled))
{
var editType = EditMode.editType;
EditorGUI.BeginChangeCheck();
var mixIcon = editType == EditMode.EditType.Mix ? DirectorStyles.mixOn : DirectorStyles.mixOff;
GUILayout.Toggle(editType == EditMode.EditType.Mix, mixIcon, DirectorStyles.Instance.editModeBtn);
if (EditorGUI.EndChangeCheck())
EditMode.editType = EditMode.EditType.Mix;
EditorGUI.BeginChangeCheck();
var rippleIcon = editType == EditMode.EditType.Ripple ? DirectorStyles.rippleOn : DirectorStyles.rippleOff;
GUILayout.Toggle(editType == EditMode.EditType.Ripple, rippleIcon, DirectorStyles.Instance.editModeBtn);
if (EditorGUI.EndChangeCheck())
EditMode.editType = EditMode.EditType.Ripple;
EditorGUI.BeginChangeCheck();
var replaceIcon = editType == EditMode.EditType.Replace ? DirectorStyles.replaceOn : DirectorStyles.replaceOff;
GUILayout.Toggle(editType == EditMode.EditType.Replace, replaceIcon, DirectorStyles.Instance.editModeBtn);
if (EditorGUI.EndChangeCheck())
EditMode.editType = EditMode.EditType.Replace;
}
}
// Draws the box to enter the time field
void TimeCodeGUI()
{
const string timeFieldHint = "TimelineWindow-TimeCodeGUI";
EditorGUI.BeginChangeCheck();
var currentTime = state.editSequence.asset != null ? TimeReferenceUtility.ToTimeString(state.editSequence.time, "0.####") : "0";
var r = EditorGUILayout.GetControlRect(false, EditorGUI.kSingleLineHeight, EditorStyles.toolbarTextField, GUILayout.Width(WindowConstants.timeCodeWidth));
var id = GUIUtility.GetControlID(timeFieldHint.GetHashCode(), FocusType.Keyboard, 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();
state.timeReferenceMode = (TimeReferenceMode)EditorGUILayout.CycleButton((int)state.timeReferenceMode, k_TimeReferenceGUIContents, DirectorStyles.Instance.timeReferenceButton);
if (EditorGUI.EndChangeCheck())
OnTimeReferenceModeChanged();
}
void OnTimeReferenceModeChanged()
{
m_TimeAreaDirty = true;
InitTimeAreaFrameRate();
SyncTimeAreaShownRange();
foreach (var inspector in InspectorWindow.GetAllInspectorWindows())
{
inspector.Repaint();
}
}
void DrawHeaderEditButtons()
{
if (state.editSequence.asset == null)
return;
using (new GUILayout.HorizontalScope(EditorStyles.toolbar, GUILayout.Width(sequenceHeaderRect.width)))
{
GUILayout.Space(DirectorStyles.kBaseIndent);
AddButtonGUI();
GUILayout.FlexibleSpace();
EditModeToolbarGUI(currentMode);
ShowMarkersButton();
EditorGUILayout.Space();
}
}
}
}

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,42 @@
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 HeaderSplitterManipulator());
m_PreTreeViewControl.AddManipulator(new TimelinePanManipulator());
m_PreTreeViewControl.AddManipulator(new TrackResize());
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 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 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 MarkerHeaderTrackManipulator());
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,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
/// <summary>
/// The public Breadcrumb navigation controller, accessible through TimelineEditorWindow
/// </summary>
public override TimelineNavigator navigator => new TimelineNavigator(this);
/// <summary>
/// Implementation of TimelineNavigator
/// </summary>
/// <remarks>
/// Always use TimelineNavigator, not this class.
///
/// The class acts as a handle on the TimelineWindow, and lets users navigate the breadcrumbs and dive into subtimelines
/// </remarks>
internal class TimelineNavigatorImpl
{
/// <summary>
///
/// </summary>
/// <param name="window"></param>
public TimelineNavigatorImpl(IWindowStateProvider window)
{
if (window == null)
throw new ArgumentNullException(nameof(window),
"TimelineNavigator cannot be used with a null window");
m_Window = window;
}
/// <summary>
/// Creates a SequenceContext from the top of the breadcrumb stack
/// </summary>
/// <returns></returns>
public SequenceContext GetCurrentContext()
{
return GetBreadcrumbs().LastOrDefault();
}
/// <summary>
/// Creates a SequenceContext from the second to last breadcrumb in the list
/// </summary>
/// <returns>Valid context if there is a parent, Invalid context otherwise</returns>
public SequenceContext GetParentContext()
{
//If the edit sequence is the master sequence, there is no parent context
if (windowState.editSequence == windowState.masterSequence)
return SequenceContext.Invalid;
var contexts = GetBreadcrumbs();
var length = contexts.Count();
return contexts.ElementAtOrDefault(length - 2);
}
/// <summary>
/// Creates a SequenceContext from the top of the breadcrumb stack
/// </summary>
/// <returns>Always returns a valid SequenceContext</returns>
public SequenceContext GetRootContext()
{
return GetBreadcrumbs().FirstOrDefault();
}
/// <summary>
/// Creates SequenceContexts for all the child Timelines
/// </summary>
/// <returns>Collection of SequenceContexts. Can be empty if there are no valid child contexts</returns>
public IEnumerable<SequenceContext> GetChildContexts()
{
return windowState.GetSubSequences();
}
/// <summary>
/// Creates SequenceContexts from the breadcrumb stack, from top to bottom
/// </summary>
/// <returns>Collection of SequenceContexts. Should never be empty</returns>
public IEnumerable<SequenceContext> GetBreadcrumbs()
{
return CollectBreadcrumbContexts();
}
/// <summary>
/// Changes the current Timeline shown in the TimelineWindow to a new SequenceContext (if different and valid)
/// </summary>
/// <remarks>
/// Should only ever accept SequenceContexts that are in the breadcrumbs. SetTimeline is the proper
/// method to use to switch root Timelines.
/// </remarks>
/// <param name="context">A valid SequenceContext. <paramref name="context"/> should always be found in the breadcrumbs</param>
/// <exception cref="System.ArgumentException"> The context is not valid</exception>
/// <exception cref="System.InvalidOperationException"> The context is not a valid navigation destination.</exception>
public void NavigateTo(SequenceContext context)
{
if (!context.IsValid())
throw new ArgumentException(
$"Argument {nameof(context)} is not valid. Check validity with SequenceContext.IsValid.");
//If the provided context is the current context
if (windowState.editSequence.hostClip == context.clip &&
windowState.editSequence.director == context.director &&
windowState.editSequence.asset == context.director.playableAsset)
{
return; // Nothing to do
}
if (context.clip == null)
{
if (context.director != windowState.masterSequence.director)
throw new InvalidOperationException($"{nameof(context)} is not a valid destination in this context. " +
$"To change the root context, use TimelineEditorWindow.SetTimeline instead.");
}
var children = GetChildContexts().ToArray();
var breadcrumbs = CollectBreadcrumbContexts().ToArray();
if (!children.Contains(context) && !breadcrumbs.Contains(context))
{
throw new InvalidOperationException(
"The provided SequenceContext is not a valid destination. " +
"Use GetChildContexts or GetBreadcrumbs to acquire valid destination contexts.");
}
if (children.Contains(context))
{
windowState.SetCurrentSequence(context.director.playableAsset as TimelineAsset, context.director, context.clip);
return;
}
var idx = Array.IndexOf(breadcrumbs, context);
if (idx != -1)
{
windowState.PopSequencesUntilCount(idx + 1);
}
}
private IWindowState windowState
{
get
{
if (m_Window == null || m_Window.windowState == null)
throw new InvalidOperationException("The Window associated to this instance has been destroyed");
return m_Window.windowState;
}
}
private IEnumerable<SequenceContext> CollectBreadcrumbContexts()
{
return windowState.allSequences?.Select(s => new SequenceContext(s.director, s.hostClip));
}
private readonly IWindowStateProvider m_Window;
}
}
}

View File

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

View File

@ -0,0 +1,132 @@
using System;
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)
{
double minimumPlayRangeTime = 0.01;
double t0 = Math.Max(0.0f, state.PixelToTime(state.timeAreaRect.xMin));
double t1 = Math.Min(state.masterSequence.duration, state.PixelToTime(state.timeAreaRect.xMax));
if (Math.Abs(t1 - t0) <= minimumPlayRangeTime)
{
state.playRange = new PlayRange(t0, t1);
return;
}
double deltaT = (t1 - t0) * 0.25 / 2.0;
t0 += deltaT;
t1 -= deltaT;
if (t1 < t0)
{
double temp = t0;
t0 = t1;
t1 = temp;
}
if (Math.Abs(t1 - t0) < minimumPlayRangeTime)
{
if (t0 - minimumPlayRangeTime > 0.0f)
t0 -= minimumPlayRangeTime;
else if (t1 + minimumPlayRangeTime < state.masterSequence.duration)
t1 += minimumPlayRangeTime;
}
state.playRange = new PlayRange(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.start);
m_PlayRangeEnd.Draw(sequenceContentRect, state, playRangeTime.end);
// Draw Time Range Box from Start to End...
if (state.playRangeEnabled && m_PlayHead != null)
{
Rect rect =
Rect.MinMaxRect(
Mathf.Clamp(state.TimeToPixel(playRangeTime.start), state.timeAreaRect.xMin, state.timeAreaRect.xMax),
m_PlayHead.bounds.yMax,
Mathf.Clamp(state.TimeToPixel(playRangeTime.end), 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)
{
PlayRange range = state.playRange;
range.start = newTime;
state.playRange = range;
m_PlayRangeStart.showTooltip = true;
}
void OnTrackHeadMaxSelectDrag(double newTime)
{
PlayRange range = state.playRange;
range.end = 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.GetParentTrack().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,105 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
/// <summary>
/// Internal class that implements TimelinePlaybackControls
/// </summary>
internal class TimelinePlaybackControlsImpl : ITimelinePlaybackControls
{
public TimelinePlaybackControlsImpl(IWindowStateProvider window)
{
if (window == null)
throw new ArgumentNullException(nameof(window),
"TimelineNavigator cannot be used with a null window");
m_Window = window;
}
public void Play()
{
windowState.SetPlaying(true);
}
public void Pause()
{
windowState.SetPlaying(false);
}
public void PreviousFrame()
{
windowState.editSequence.frame--;
}
public void NextFrame()
{
windowState.editSequence.frame++;
}
public void GoToFirstFrame()
{
windowState.editSequence.time = 0;
}
public void GoToLastFrame()
{
windowState.editSequence.time = windowState.editSequence.duration;
}
public void SetCurrentTime(double time, TimelinePlaybackControls.Context context)
{
ISequenceState targetSequenceState = GetTargetSequenceState(context);
targetSequenceState.time = time;
}
public void SetCurrentFrame(int frame, TimelinePlaybackControls.Context context)
{
ISequenceState targetSequenceState = GetTargetSequenceState(context);
targetSequenceState.frame = frame;
}
public double GetCurrentTime(TimelinePlaybackControls.Context context)
{
ISequenceState targetSequenceState = GetTargetSequenceState(context);
return targetSequenceState.time;
}
public int GetCurrentFrame(TimelinePlaybackControls.Context context)
{
ISequenceState targetSequenceState = GetTargetSequenceState(context);
return targetSequenceState.frame;
}
ISequenceState GetTargetSequenceState(TimelinePlaybackControls.Context context)
{
switch (context)
{
case TimelinePlaybackControls.Context.Global:
return windowState.masterSequence;
case TimelinePlaybackControls.Context.Local:
return windowState.editSequence;
default:
throw new ArgumentException("Unknown Context", nameof(context));
}
}
IWindowState windowState
{
get
{
if (m_Window == null || m_Window.windowState == null)
throw new InvalidOperationException("The Window associated to this instance has been destroyed");
return m_Window.windowState;
}
}
readonly IWindowStateProvider m_Window;
}
public override TimelinePlaybackControls playbackControls => new TimelinePlaybackControls(this);
}
}

View File

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

View File

@ -0,0 +1,84 @@
using System;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
void OnPreviewPlayModeChanged(bool isPlaying)
{
if (state != null && !state.ignorePreview && 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.end) < TimeUtility.kFrameRateEpsilon || time > playRangeTime.end || time < playRangeTime.start)
{
state.masterSequence.time = playRangeTime.start;
// 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,92 @@
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline
{
partial class TimelineWindow
{
[SerializeField]
SequencePath m_SequencePath;
void OnSelectionChange()
{
//Sanitize the inline curve selection
SelectionManager.GetCurrentInlineEditorCurve()?.ValidateCurvesSelection();
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;
}
}
//If not currently editing a Timeline and the selection is empty, clear selection
if (Selection.activeObject == null &&
state.IsEditingAnEmptyTimeline())
{
SetCurrentSelection(null);
}
// otherwise, keep the same selection.
RestoreLastSelection(forceRebuild);
}
void RestoreLastSelection(bool forceRebuild)
{
state.SetCurrentSequencePath(m_SequencePath, forceRebuild);
//case 1201405 and 1278598: unlock the window if there is no valid asset, since the lock button is disabled
if (m_LockTracker.isLocked && state.editSequence.asset == null)
m_LockTracker.isLocked = false;
}
void SetCurrentSelection(Object obj)
{
var selectedGameObject = obj as GameObject;
if (selectedGameObject != null)
{
PlayableDirector director = TimelineUtility.GetDirectorComponentForGameObject(selectedGameObject);
SetTimeline(director);
}
else
{
var selectedSequenceAsset = obj as TimelineAsset;
if (selectedSequenceAsset != null)
{
SetTimeline(selectedSequenceAsset);
}
}
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,56 @@
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();
}
if (state.ignorePreview && state.IsPlayableGraphDone())
state.Pause();
// 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}");
}
double 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 DrawTimelineRuler()
{
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, (float)state.referenceSequence.frameRate, true, false, 1.0f, state.timeFormat.ToTimeAreaFormat());
m_TimeArea.EndViewGUI();
}
void InitTimeAreaFrameRate()
{
m_LastFrameRate = state.referenceSequence.frameRate;
m_TimeArea.hTicks.SetTickModulosForFrameRate((float)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,81 @@
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));
}
}
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,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
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.headerSpacePartitioner.Clear();
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, (float)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, L10n.Tr("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(L10n.Tr("Create"), GUILayout.Width(WindowConstants.createButtonWidth)))
{
var message = DirectorStyles.createNewTimelineText.text + " '" + currentlySelectedGo.name + "'";
var defaultName = currentlySelectedGo.name.EndsWith(DirectorStyles.newTimelineDefaultNameSuffix, StringComparison.OrdinalIgnoreCase)
? currentlySelectedGo.name
: currentlySelectedGo.name + DirectorStyles.newTimelineDefaultNameSuffix;
// Use the project window path by default only if it's under the asset folder.
// Otherwise the saveFilePanel will reject the save (case 1289923)
var defaultPath = ProjectWindowUtil.GetActiveFolderPath();
if (!defaultPath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
defaultPath = "Assets";
string newSequencePath = EditorUtility.SaveFilePanelInProject(DirectorStyles.createNewTimelineText.text, defaultName, "playable", message, defaultPath);
if (!string.IsNullOrEmpty(newSequencePath))
{
var newAsset = TimelineUtility.CreateAndSaveTimelineAsset(newSequencePath);
Undo.IncrementCurrentGroup();
if (existingDirector == null)
{
existingDirector = Undo.AddComponent<PlayableDirector>(currentlySelectedGo);
}
existingDirector.playableAsset = newAsset;
SetTimeline(existingDirector);
windowState.previewMode = false;
}
// If we reach this point, the state of the panel 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();
}
internal List<OverlayDrawer> OverlayDrawData = new List<OverlayDrawer>();
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))
overlayData.Draw();
}
}
}
GUILayout.EndVertical();
}
}
}

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,126 @@
using System;
using System.Collections.Generic;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Serialization;
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 int trackHeightExtension;
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]
partial 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 PlayRange NoPlayRangeSet = new PlayRange(double.MaxValue, double.MaxValue);
public Vector2 timeAreaShownRange = TimeAreaDefaultRange;
public float trackScale = DefaultTrackScale;
public bool playRangeEnabled;
public PlayRange timeAreaPlayRange
{
get { return m_TimeAreaPlayRange; }
set { m_TimeAreaPlayRange = value; }
}
public double windowTime;
public float verticalScroll = DefaultVerticalScroll;
public float sequencerHeaderWidth = WindowConstants.defaultHeaderWidth;
public Dictionary<TrackAsset, TrackViewModelData> tracksViewModelData = new Dictionary<TrackAsset, TrackViewModelData>();
[SerializeField] PlayRange m_TimeAreaPlayRange;
// 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 deserialized
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()
{
UpgradeIfNecessary();
m_Version = (int)Versions.Current;
}
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,37 @@
using UnityEngine;
using UnityEngine.Serialization;
namespace UnityEditor.Timeline
{
partial class TimelineAssetViewModel
{
//used for file format versioning purposes
[SerializeField] int m_Version;
#pragma warning disable 649
[FormerlySerializedAs("timeAreaPlayRange")]
[SerializeField]
Vector2 deprecated_timeAreaPlayRange;
#pragma warning restore 649
enum Versions
{
Default = 0,
PlayRangeAsStruct = 1,
Current = PlayRangeAsStruct
}
void UpgradeIfNecessary()
{
if (m_Version < (int)Versions.PlayRangeAsStruct)
Upgrade_PlayRangeAsStruct();
m_Version = (int)Versions.Current;
}
void Upgrade_PlayRangeAsStruct()
{
m_TimeAreaPlayRange = new PlayRange(deprecated_timeAreaPlayRange.x, deprecated_timeAreaPlayRange.y);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c0826daeb1e45578e12fbbd2b89b638
timeCreated: 1622558571

View File

@ -0,0 +1,184 @@
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;
}
public static int GetTrackHeightExtension(TrackAsset asset)
{
if (asset == null)
return 0;
return GetTrackViewModelData(asset).trackHeightExtension;
}
public static void SetTrackHeightExtension(TrackAsset asset, int height)
{
if (asset != null)
GetTrackViewModelData(asset).trackHeightExtension = 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,47 @@
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 minHeaderWidth = 195.0f;
public const float maxHeaderWidth = 650.0f;
public const float headerSplitterWidth = 6.0f;
public const float headerSplitterVisualWidth = 2.0f;
public const float maxTimeAreaScaling = 90000.0f;
public const float timeCodeWidth = 100.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 selectorWidth = 23.0f;
public const float cogButtonWidth = 25.0f;
public const float trackHeaderBindingHeight = 18.0f;
public const float trackHeaderButtonSize = 16.0f;
public const float trackHeaderButtonPadding = 2.0f;
public const float trackBindingMaxSize = 300.0f;
public const float trackBindingPadding = 5.0f;
public const float trackInsertionMarkerHeight = 1f;
public const float trackResizeHandleHeight = 7f;
public const float inlineCurveContentPadding = 2.0f;
public const float playControlsWidth = 300;
public const int autoPanPaddingInPixels = 50;
public const float overlayTextPadding = 40.0f;
}
}

View File

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