Unity-jump-proj
This commit is contained in:
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37472f5179ca2004489ac901814cdbc3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
enum TimeReferenceMode
|
||||
{
|
||||
Local = 0,
|
||||
Global = 1
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34d6f60b171c1004e8335d52c65928a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67ee43b2f6148de40861b289b0e00591
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3477d28057cb3e4469c7ea6b8dc23046
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c5eb52d37bb6714a98af73df7d9cf2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5503f95d174761548a68a901beab13c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2cb43d6b0c226443be7e176590837a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f8643c1f8dd449e85b548a14edbea2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f4398a10f384dacb37698e5b1f8fede
|
||||
timeCreated: 1597264928
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98545765d7a2b614b921715928035ee2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ad2b855cf8b7425483e36e6134f18f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfe0d35963084dcc899e4d32cca85455
|
||||
timeCreated: 1590671897
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4068e97704a16794ea218ba560cdc1e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c756627eb1aed7a40bd8ba981a07ede5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b662f155e14546d0b72f129800d013d0
|
||||
timeCreated: 1620849473
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f817a38900380be47942905e17e7d39b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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('_');
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d4de78ba9fbd472a884ea157f740743
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f25fb081e85cb743b272c2f7fbc2f6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7abcd1a72bb7174ca58e813c6eee9c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2cd16a2d73fe7a4c9affa2b790eb5e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b3bd7a976306c9449ba84e0591e8a0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1c9c1ed454d0594b951eb6a76ac62ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a956e5803e95df349bd35832492d4014
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdd19c82588da3e498a0c98951efa6c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3a595c9a8ed19040bb2612fe168759d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8716cfd7a46000048acf638abe28c9fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14d748c963c7b3549bed45457cc92c4f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3168bf9e060ff4b46be4bf08e308ce97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb2f3c5bf79e22046b2e3866dc3acb99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c9c9f62af2efb948a1974650039e2db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16548db454f7a3344b41ca2e5cdb52b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b926af22079e00e4a8c073321194cea1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b846f69b139b3a341a5699a09fa52b2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e763a90581e2d8143bc9a0e384ce6f0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08d23c0b73905c148b525c3c93fff580
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9d7bb79ed0c2854a8a5ed7decc3e44f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80ae83fdf1fb2c649bccb8c293b94556
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d79cb9ecc0d4a6d428ab98a681a33897
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c0826daeb1e45578e12fbbd2b89b638
|
||||
timeCreated: 1622558571
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 105515c1653548242b4fe973c0f375f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d768b1bb52e2c64ba818933dbdd8452
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user