Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -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,36 @@
|
|||
using UnityEngine.Playables;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class of the TimelineWindow.
|
||||
/// </summary>
|
||||
public abstract class TimelineEditorWindow : EditorWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows retrieving and and setting the Timeline Window lock state. When the lock is off, the window focus follows the Unity selection.
|
||||
/// </summary>
|
||||
/// <remarks>When lock transitions from true to false, the focused timeline will be synchronized with the Unity selection.</remarks>>
|
||||
public abstract bool locked { get; set; }
|
||||
/// <summary>
|
||||
/// Allows setting which TimelineAsset is shown in the TimelineWindow.
|
||||
/// </summary>
|
||||
/// <param name="sequence">The asset to show.</param>
|
||||
/// <remarks>Calling this method will put the window in asset edit mode and certain features might be missing (eg: timeline cannot be evaluated, bindings will not be available, etc).
|
||||
/// Ignores window lock mode. Calling with null, will clear the displayed timeline.</remarks>
|
||||
public abstract void SetTimeline(TimelineAsset sequence);
|
||||
/// <summary>
|
||||
/// Allows setting which TimelineAsset is shown in the TimelineWindow and which PlayableDirector is used to evaluate it.
|
||||
/// </summary>
|
||||
/// <param name="director">The PlayableDirector who's timeline should be shown.</param>
|
||||
/// <remarks>Ignores window lock mode. Calling with null, will clear the displayed timeline.</remarks>
|
||||
public abstract void SetTimeline(PlayableDirector director);
|
||||
|
||||
/// <summary>
|
||||
/// Allows clearing the TimelineAsset that is shown in the TimelineWindow.
|
||||
/// </summary>
|
||||
/// <remarks>Ignores window lock mode.</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,519 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.Playables;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
[EditorWindowTitle(title = "Timeline", useTypeNameAsIconName = true)]
|
||||
partial class TimelineWindow : TimelineEditorWindow, IHasCustomMenu
|
||||
{
|
||||
[Serializable]
|
||||
public class TimelineWindowPreferences
|
||||
{
|
||||
public bool playRangeLoopMode = true;
|
||||
public EditMode.EditType editType = EditMode.EditType.Mix;
|
||||
public TimeReferenceMode timeReferenceMode = TimeReferenceMode.Local;
|
||||
}
|
||||
|
||||
[SerializeField] TimelineWindowPreferences m_Preferences = new TimelineWindowPreferences();
|
||||
public TimelineWindowPreferences preferences { get { return m_Preferences; } }
|
||||
|
||||
[SerializeField]
|
||||
EditorGUIUtility.EditorLockTracker m_LockTracker = new EditorGUIUtility.EditorLockTracker();
|
||||
|
||||
readonly PreviewResizer m_PreviewResizer = new PreviewResizer();
|
||||
bool m_LastFrameHadSequence;
|
||||
bool m_ForceRefreshLastSelection;
|
||||
int m_CurrentSceneHashCode = -1;
|
||||
|
||||
[NonSerialized]
|
||||
bool m_HasBeenInitialized;
|
||||
|
||||
[SerializeField]
|
||||
SequenceHierarchy m_SequenceHierarchy;
|
||||
static SequenceHierarchy s_LastHierarchy;
|
||||
|
||||
public static TimelineWindow instance { get; private set; }
|
||||
public Rect clientArea { get; set; }
|
||||
public bool isDragging { get; set; }
|
||||
public static DirectorStyles styles { get { return DirectorStyles.Instance; } }
|
||||
public List<TimelineTrackBaseGUI> allTracks
|
||||
{
|
||||
get
|
||||
{
|
||||
return treeView != null ? treeView.allTrackGuis : new List<TimelineTrackBaseGUI>();
|
||||
}
|
||||
}
|
||||
|
||||
public WindowState state { get; private set; }
|
||||
|
||||
public 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();
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
state.editSequence.ResetIsReadOnly(); //Force reset readonly for asset flag for each frame.
|
||||
// detect if the sequence was removed under our feet
|
||||
if (m_LastFrameHadSequence && state.editSequence.asset == null)
|
||||
{
|
||||
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 || 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,314 @@
|
|||
using System;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
class TimelineWindowTimeControl : IAnimationWindowControl
|
||||
{
|
||||
[Serializable]
|
||||
public struct ClipData
|
||||
{
|
||||
public double start;
|
||||
public double duration;
|
||||
public TrackAsset track;
|
||||
}
|
||||
|
||||
[SerializeField] ClipData m_ClipData;
|
||||
[SerializeField] TimelineClip m_Clip;
|
||||
[SerializeField] AnimationWindowState m_AnimWindowState;
|
||||
|
||||
TrackAsset track
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Clip != null)
|
||||
{
|
||||
return m_Clip.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), state.referenceSequence.frameRate);
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeTime(float newTime)
|
||||
{
|
||||
if (state != null && state.editSequence.director != null)
|
||||
{
|
||||
// avoid rounding errors
|
||||
var finalTime = ToGlobalTime(newTime);
|
||||
if (TimeUtility.OnFrameBoundary(finalTime, state.referenceSequence.frameRate, TimeUtility.kFrameRateEpsilon))
|
||||
finalTime = TimeUtility.RoundToFrame(finalTime, state.referenceSequence.frameRate);
|
||||
state.editSequence.time = finalTime;
|
||||
|
||||
window.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
static void ChangeFrame(int frame)
|
||||
{
|
||||
if (state != null)
|
||||
{
|
||||
state.editSequence.frame = frame;
|
||||
window.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public override void GoToTime(float newTime)
|
||||
{
|
||||
ChangeTime(newTime);
|
||||
}
|
||||
|
||||
public override void GoToFrame(int frame)
|
||||
{
|
||||
ChangeFrame(frame);
|
||||
}
|
||||
|
||||
public override void StartScrubTime() {}
|
||||
|
||||
public override void EndScrubTime() {}
|
||||
|
||||
public override void ScrubTime(float newTime)
|
||||
{
|
||||
ChangeTime(newTime);
|
||||
}
|
||||
|
||||
public override void GoToPreviousFrame()
|
||||
{
|
||||
if (state != null)
|
||||
ChangeFrame(state.editSequence.frame - 1);
|
||||
}
|
||||
|
||||
public override void GoToNextFrame()
|
||||
{
|
||||
if (state != null)
|
||||
ChangeFrame(state.editSequence.frame + 1);
|
||||
}
|
||||
|
||||
AnimationWindowCurve[] GetCurves()
|
||||
{
|
||||
var curves =
|
||||
(m_AnimWindowState.showCurveEditor &&
|
||||
m_AnimWindowState.activeCurves.Count > 0) ? m_AnimWindowState.activeCurves : m_AnimWindowState.allCurves;
|
||||
return curves.ToArray();
|
||||
}
|
||||
|
||||
public override void GoToPreviousKeyframe()
|
||||
{
|
||||
var newTime = AnimationWindowUtility.GetPreviousKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate);
|
||||
GoToTime(m_AnimWindowState.SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToClipFrame));
|
||||
}
|
||||
|
||||
public override void GoToNextKeyframe()
|
||||
{
|
||||
var newTime = AnimationWindowUtility.GetNextKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate);
|
||||
GoToTime(m_AnimWindowState.SnapToFrame(newTime, AnimationWindowState.SnapMode.SnapToClipFrame));
|
||||
}
|
||||
|
||||
public override void GoToFirstKeyframe()
|
||||
{
|
||||
GoToTime(0);
|
||||
}
|
||||
|
||||
public override void GoToLastKeyframe()
|
||||
{
|
||||
double animClipTime = 0;
|
||||
if (m_Clip != null)
|
||||
{
|
||||
var curves = m_Clip.curves;
|
||||
var animAsset = m_Clip.asset as AnimationPlayableAsset;
|
||||
if (animAsset != null)
|
||||
{
|
||||
animClipTime = animAsset.clip != null ? animAsset.clip.length : 0;
|
||||
}
|
||||
else if (curves != null)
|
||||
{
|
||||
animClipTime = curves.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
animClipTime = m_Clip.clipAssetDuration;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
animClipTime = m_ClipData.duration;
|
||||
}
|
||||
|
||||
GoToTime((float)animClipTime);
|
||||
}
|
||||
|
||||
public override bool canPlay
|
||||
{
|
||||
get
|
||||
{
|
||||
return state != null && state.previewMode;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool playing
|
||||
{
|
||||
get
|
||||
{
|
||||
return state != null && state.playing;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetPlaybackState(bool playbackState)
|
||||
{
|
||||
if (state == null || playbackState == state.playing)
|
||||
return;
|
||||
|
||||
state.SetPlaying(playbackState);
|
||||
}
|
||||
|
||||
public override bool StartPlayback()
|
||||
{
|
||||
SetPlaybackState(true);
|
||||
return state != null && state.playing;
|
||||
}
|
||||
|
||||
public override void StopPlayback()
|
||||
{
|
||||
SetPlaybackState(false);
|
||||
}
|
||||
|
||||
public override bool PlaybackUpdate() { return state != null && state.playing; }
|
||||
|
||||
public override bool canRecord
|
||||
{
|
||||
get { return state != null && state.canRecord; }
|
||||
}
|
||||
|
||||
public override bool recording
|
||||
{
|
||||
get { return state != null && state.recording; }
|
||||
}
|
||||
|
||||
public override bool canPreview
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool previewing
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool StartRecording(Object targetObject)
|
||||
{
|
||||
if (!canRecord)
|
||||
return false;
|
||||
|
||||
if (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,85 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,325 @@
|
|||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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 || state.rebuildGraph)
|
||||
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.MarkDirty();
|
||||
}
|
||||
else if (playableAsset != null)
|
||||
{
|
||||
var clip = TimelineRecording.FindClipWithAsset(state.editSequence.asset, playableAsset);
|
||||
if (clip != null)
|
||||
clip.MarkDirty();
|
||||
}
|
||||
else if (editorClip != null && editorClip.clip != null)
|
||||
{
|
||||
editorClip.clip.MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UndoPropertyModification[] ProcessAssetModifications(UndoPropertyModification[] modifications)
|
||||
{
|
||||
bool rebuildGraph = false;
|
||||
|
||||
for (int i = 0; i < modifications.Length && !rebuildGraph; i++)
|
||||
{
|
||||
var mod = modifications[i];
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d1c9c1ed454d0594b951eb6a76ac62ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,472 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
using UnityEditor.SceneManagement;
|
||||
#else
|
||||
using UnityEditor.Experimental.SceneManagement;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
partial class TimelineWindow
|
||||
{
|
||||
struct MarkerOverlay
|
||||
{
|
||||
public IMarker marker;
|
||||
public Rect rect;
|
||||
public bool isSelected;
|
||||
public bool isCollapsed;
|
||||
public MarkerEditor editor;
|
||||
}
|
||||
|
||||
|
||||
enum TimelineItemArea
|
||||
{
|
||||
Header,
|
||||
Lines
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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 standardFrameRate = false;
|
||||
for (int i = 0; i < TimelineProjectSettings.framerateValues.Length; i++)
|
||||
{
|
||||
standardFrameRate |= AddStandardFrameRateMenu(menu, "Frame Rate/" + TimelineProjectSettings.framerateLabels[i], TimelineProjectSettings.framerateValues[i]);
|
||||
}
|
||||
|
||||
if (standardFrameRate)
|
||||
menu.AddDisabledItem(L10n.TextContent("Frame Rate/Custom"));
|
||||
else
|
||||
menu.AddItem(L10n.TextContent("Frame Rate/Custom (" + state.editSequence.frameRate + ")"), true, () => {});
|
||||
|
||||
menu.AddSeparator("");
|
||||
if (state.playRangeEnabled)
|
||||
{
|
||||
menu.AddItem(L10n.TextContent("Play Range Mode/Loop"), state.playRangeLoopMode, () => state.playRangeLoopMode = true);
|
||||
menu.AddItem(L10n.TextContent("Play Range Mode/Once"), !state.playRangeLoopMode, () => state.playRangeLoopMode = false);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddDisabledItem(L10n.TextContent("Play Range Mode"));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AddStandardFrameRateMenu(GenericMenu menu, string name, float value)
|
||||
{
|
||||
bool on = state.editSequence.frameRate.Equals(value);
|
||||
if (state.editSequence.isReadOnly)
|
||||
{
|
||||
menu.AddDisabledItem(EditorGUIUtility.TextContent(name), on);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddItem(EditorGUIUtility.TextContent(name), on, r =>
|
||||
{
|
||||
state.editSequence.frameRate = value;
|
||||
}, value);
|
||||
}
|
||||
return on;
|
||||
}
|
||||
|
||||
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, "f4") : "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,131 @@
|
|||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
partial class TimelineWindow
|
||||
{
|
||||
TimeAreaItem m_PlayRangeEnd;
|
||||
TimeAreaItem m_PlayRangeStart;
|
||||
|
||||
void PlayRangeGUI(TimelineItemArea area)
|
||||
{
|
||||
if (!currentMode.ShouldShowPlayRange(state) || treeView == null)
|
||||
return;
|
||||
|
||||
if (state.masterSequence.asset != null && !state.masterSequence.asset.GetRootTracks().Any())
|
||||
return;
|
||||
|
||||
// left Time Cursor
|
||||
if (m_PlayRangeStart == null || m_PlayRangeStart.style != styles.playTimeRangeStart)
|
||||
{
|
||||
m_PlayRangeStart = new TimeAreaItem(styles.playTimeRangeStart, OnTrackHeadMinSelectDrag);
|
||||
Vector2 offset = new Vector2(-2.0f, 0);
|
||||
m_PlayRangeStart.boundOffset = offset;
|
||||
}
|
||||
|
||||
// right Time Cursor
|
||||
if (m_PlayRangeEnd == null || m_PlayRangeEnd.style != styles.playTimeRangeEnd)
|
||||
{
|
||||
m_PlayRangeEnd = new TimeAreaItem(styles.playTimeRangeEnd, OnTrackHeadMaxSelectDrag);
|
||||
Vector2 offset = new Vector2(2.0f, 0);
|
||||
m_PlayRangeEnd.boundOffset = offset;
|
||||
}
|
||||
|
||||
if (area == TimelineItemArea.Header)
|
||||
DrawPlayRange(true, false);
|
||||
else if (area == TimelineItemArea.Lines)
|
||||
DrawPlayRange(false, true);
|
||||
}
|
||||
|
||||
void DrawPlayRange(bool drawHeads, bool drawLines)
|
||||
{
|
||||
Rect timeCursorRect = state.timeAreaRect;
|
||||
timeCursorRect.height = clientArea.height;
|
||||
|
||||
m_PlayRangeEnd.HandleManipulatorsEvents(state);
|
||||
m_PlayRangeStart.HandleManipulatorsEvents(state);
|
||||
|
||||
// The first time a user enable the play range, we put the play range 75% around the current time...
|
||||
if (state.playRange == TimelineAssetViewModel.NoPlayRangeSet)
|
||||
{
|
||||
float minimumPlayRangeTime = 0.01f;
|
||||
float t0 = Mathf.Max(0.0f, state.PixelToTime(state.timeAreaRect.xMin));
|
||||
float t1 = Mathf.Min((float)state.masterSequence.duration, state.PixelToTime(state.timeAreaRect.xMax));
|
||||
|
||||
if (Mathf.Abs(t1 - t0) <= minimumPlayRangeTime)
|
||||
{
|
||||
state.playRange = new Vector2(t0, t1);
|
||||
return;
|
||||
}
|
||||
|
||||
float deltaT = (t1 - t0) * 0.25f / 2.0f;
|
||||
|
||||
t0 += deltaT;
|
||||
t1 -= deltaT;
|
||||
|
||||
if (t1 < t0)
|
||||
{
|
||||
float temp = t0;
|
||||
t0 = t1;
|
||||
t1 = temp;
|
||||
}
|
||||
|
||||
if (Mathf.Abs(t1 - t0) < minimumPlayRangeTime)
|
||||
{
|
||||
if (t0 - minimumPlayRangeTime > 0.0f)
|
||||
t0 -= minimumPlayRangeTime;
|
||||
else if (t1 + minimumPlayRangeTime < state.masterSequence.duration)
|
||||
t1 += minimumPlayRangeTime;
|
||||
}
|
||||
|
||||
state.playRange = new Vector2(t0, t1);
|
||||
}
|
||||
|
||||
// Draw the head or the lines according to the parameters..
|
||||
m_PlayRangeStart.drawHead = drawHeads;
|
||||
m_PlayRangeStart.drawLine = drawLines;
|
||||
|
||||
m_PlayRangeEnd.drawHead = drawHeads;
|
||||
m_PlayRangeEnd.drawLine = drawLines;
|
||||
|
||||
var playRangeTime = state.playRange;
|
||||
m_PlayRangeStart.Draw(sequenceContentRect, state, playRangeTime.x);
|
||||
m_PlayRangeEnd.Draw(sequenceContentRect, state, playRangeTime.y);
|
||||
|
||||
// Draw Time Range Box from Start to End...
|
||||
if (state.playRangeEnabled && m_PlayHead != null)
|
||||
{
|
||||
Rect rect =
|
||||
Rect.MinMaxRect(
|
||||
Mathf.Clamp(state.TimeToPixel(playRangeTime.x), state.timeAreaRect.xMin, state.timeAreaRect.xMax),
|
||||
m_PlayHead.bounds.yMax,
|
||||
Mathf.Clamp(state.TimeToPixel(playRangeTime.y), state.timeAreaRect.xMin, state.timeAreaRect.xMax),
|
||||
sequenceContentRect.height + state.timeAreaRect.height + timeCursorRect.y
|
||||
);
|
||||
|
||||
|
||||
EditorGUI.DrawRect(rect, DirectorStyles.Instance.customSkin.colorRange);
|
||||
|
||||
rect.height = 3f;
|
||||
EditorGUI.DrawRect(rect, Color.white);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTrackHeadMinSelectDrag(double newTime)
|
||||
{
|
||||
Vector2 range = state.playRange;
|
||||
range.x = (float)newTime;
|
||||
state.playRange = range;
|
||||
m_PlayRangeStart.showTooltip = true;
|
||||
}
|
||||
|
||||
void OnTrackHeadMaxSelectDrag(double newTime)
|
||||
{
|
||||
Vector2 range = state.playRange;
|
||||
range.y = (float)newTime;
|
||||
state.playRange = range;
|
||||
m_PlayRangeEnd.showTooltip = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,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.y) < TimeUtility.kFrameRateEpsilon || time > playRangeTime.y || time < playRangeTime.x)
|
||||
{
|
||||
state.masterSequence.time = playRangeTime.x;
|
||||
// case 1215926 : Special case to make the director mode to play if the wrap mode is None.
|
||||
// In that mode, the engine stop the graph before we can ensure play range is respected.
|
||||
if (!state.playing && state.masterSequence.director.extrapolationMode == DirectorWrapMode.None)
|
||||
state.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5c9c9f62af2efb948a1974650039e2db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,84 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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}");
|
||||
}
|
||||
|
||||
float m_LastFrameRate;
|
||||
bool m_TimeAreaDirty = true;
|
||||
|
||||
void InitializeTimeArea()
|
||||
{
|
||||
if (m_TimeArea == null)
|
||||
{
|
||||
m_TimeArea = new TimelineTimeArea(state, false)
|
||||
{
|
||||
hRangeLocked = false,
|
||||
vRangeLocked = true,
|
||||
margin = 10,
|
||||
scaleWithWindow = true,
|
||||
hSlider = true,
|
||||
vSlider = false,
|
||||
hBaseRangeMin = 0.0f,
|
||||
hBaseRangeMax = WindowState.kMaxShownTime,
|
||||
hRangeMin = 0.0f,
|
||||
hScaleMax = WindowConstants.maxTimeAreaScaling,
|
||||
rect = state.timeAreaRect
|
||||
};
|
||||
|
||||
m_TimeAreaDirty = true;
|
||||
InitTimeAreaFrameRate();
|
||||
SyncTimeAreaShownRange();
|
||||
}
|
||||
}
|
||||
|
||||
void 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, state.referenceSequence.frameRate, true, false, 1.0f, state.timeFormat.ToTimeAreaFormat());
|
||||
m_TimeArea.EndViewGUI();
|
||||
}
|
||||
|
||||
void InitTimeAreaFrameRate()
|
||||
{
|
||||
m_LastFrameRate = state.referenceSequence.frameRate;
|
||||
m_TimeArea.hTicks.SetTickModulosForFrameRate(m_LastFrameRate);
|
||||
}
|
||||
|
||||
void SyncTimeAreaShownRange()
|
||||
{
|
||||
var range = state.timeAreaShownRange;
|
||||
if (!Mathf.Approximately(range.x, m_TimeArea.shownArea.x) || !Mathf.Approximately(range.y, m_TimeArea.shownArea.xMax))
|
||||
{
|
||||
// set view data onto the time area
|
||||
if (m_TimeAreaDirty)
|
||||
{
|
||||
m_TimeArea.SetShownHRange(range.x, range.y);
|
||||
m_TimeAreaDirty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// set time area data onto the view data
|
||||
state.TimeAreaChanged();
|
||||
}
|
||||
}
|
||||
|
||||
m_TimeArea.hBaseRangeMax = (float)state.editSequence.duration;
|
||||
}
|
||||
|
||||
class TimelineTimeArea : TimeArea
|
||||
{
|
||||
readonly WindowState m_State;
|
||||
|
||||
public TimelineTimeArea(WindowState state, bool minimalGUI) : base(minimalGUI)
|
||||
{
|
||||
m_State = state;
|
||||
}
|
||||
|
||||
public override string FormatTickTime(float time, float frameRate, TimeFormat timeFormat)
|
||||
{
|
||||
time = m_State.timeReferenceMode == TimeReferenceMode.Global ?
|
||||
(float)m_State.editSequence.ToGlobalTime(time) : time;
|
||||
|
||||
return FormatTime(time, frameRate, timeFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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, 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,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Timeline;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.Timeline
|
||||
{
|
||||
[Serializable]
|
||||
class TrackViewModelData : ISerializationCallbackReceiver
|
||||
{
|
||||
public static readonly float DefaultinlineAnimationCurveHeight = 100.0f;
|
||||
|
||||
public bool collapsed = true;
|
||||
public bool showMarkers = true;
|
||||
|
||||
public bool showInlineCurves = false;
|
||||
public float inlineAnimationCurveHeight = DefaultinlineAnimationCurveHeight;
|
||||
public int lastInlineCurveDataID = -1;
|
||||
public TreeViewState inlineCurvesState = null;
|
||||
public Rect inlineCurvesShownAreaInsideMargins = new Rect(1, 1, 1, 1);
|
||||
public 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]
|
||||
class TimelineAssetViewModel : ScriptableObject, ISerializationCallbackReceiver
|
||||
{
|
||||
public const float DefaultTrackScale = 1.0f;
|
||||
public const float DefaultVerticalScroll = 0;
|
||||
|
||||
public static readonly Vector2 TimeAreaDefaultRange = new Vector2(-WindowConstants.timeAreaShownRangePadding, 5.0f); // in seconds. Hack: using negative value to force the UI to have a left margin at 0.
|
||||
public static readonly Vector2 NoPlayRangeSet = new Vector2(float.MaxValue, float.MaxValue);
|
||||
|
||||
public Vector2 timeAreaShownRange = TimeAreaDefaultRange;
|
||||
public float trackScale = DefaultTrackScale;
|
||||
public bool playRangeEnabled;
|
||||
public Vector2 timeAreaPlayRange = NoPlayRangeSet;
|
||||
public double windowTime;
|
||||
public float verticalScroll = DefaultVerticalScroll;
|
||||
public float sequencerHeaderWidth = WindowConstants.defaultHeaderWidth;
|
||||
|
||||
public Dictionary<TrackAsset, TrackViewModelData> tracksViewModelData = new Dictionary<TrackAsset, TrackViewModelData>();
|
||||
|
||||
// Used only for serialization of the dictionary
|
||||
[SerializeField] List<TrackAsset> m_Keys = new List<TrackAsset>();
|
||||
[SerializeField] List<TrackViewModelData> m_Vals = new List<TrackViewModelData>();
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
m_Keys.Clear();
|
||||
m_Vals.Clear();
|
||||
foreach (var data in tracksViewModelData)
|
||||
{
|
||||
// Assets that don't save, will create nulls when deserializeds
|
||||
if (data.Key != null && data.Value != null && (data.Key.hideFlags & HideFlags.DontSave) == 0)
|
||||
{
|
||||
m_Keys.Add(data.Key);
|
||||
m_Vals.Add(data.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
if (m_Keys.Count == m_Vals.Count)
|
||||
{
|
||||
tracksViewModelData.Clear();
|
||||
for (int i = 0; i < m_Keys.Count; i++)
|
||||
{
|
||||
if (m_Keys[i] != null) // if the asset is overwritten the tracks can be null
|
||||
tracksViewModelData[m_Keys[i]] = m_Vals[i];
|
||||
}
|
||||
}
|
||||
|
||||
m_Keys.Clear();
|
||||
m_Vals.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d79cb9ecc0d4a6d428ab98a681a33897
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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:
|
Loading…
Add table
Add a link
Reference in a new issue