TimelineTrackGUI.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.IMGUI.Controls;
  5. using UnityEngine;
  6. using UnityEngine.Timeline;
  7. using UnityEngine.Playables;
  8. using Object = UnityEngine.Object;
  9. namespace UnityEditor.Timeline
  10. {
  11. class TimelineTrackGUI : TimelineGroupGUI, IClipCurveEditorOwner, IRowGUI
  12. {
  13. struct TrackDrawData
  14. {
  15. public bool m_AllowsRecording;
  16. public bool m_ShowTrackBindings;
  17. public bool m_HasBinding;
  18. public bool m_IsSubTrack;
  19. public PlayableBinding m_Binding;
  20. public Object m_TrackBinding;
  21. public Texture m_TrackIcon;
  22. public bool m_HasMarkers;
  23. }
  24. static class Styles
  25. {
  26. public static readonly GUIContent trackCurvesBtnOnTooltip = DirectorStyles.TrTextContent(string.Empty, "Hide curves view");
  27. public static readonly GUIContent trackCurvesBtnOffTooltip = DirectorStyles.TrTextContent(string.Empty, "Show curves view");
  28. public static readonly GUIContent trackMarkerBtnOnTooltip = DirectorStyles.TrTextContent(string.Empty, "Collapse Track Markers");
  29. public static readonly GUIContent trackMarkerBtnOffTooltip = DirectorStyles.TrTextContent(string.Empty, "Expand Track Markers");
  30. public static readonly GUIContent kActiveRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty, "End recording");
  31. public static readonly GUIContent kInactiveRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty, "Start recording");
  32. public static readonly GUIContent kIgnorePreviewRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty, "Recording is disabled: scene preview is ignored for this TimelineAsset");
  33. public static readonly GUIContent kDisabledRecordButtonTooltip = DirectorStyles.TrTextContent(string.Empty,
  34. "Recording is not permitted when Track Offsets are set to Auto. Track Offset settings can be changed in the track menu of the base track.");
  35. public static Texture2D kProblemIcon = DirectorStyles.GetBackgroundImage(DirectorStyles.Instance.warning);
  36. }
  37. static GUIContent s_ArmForRecordContentOn;
  38. static GUIContent s_ArmForRecordContentOff;
  39. static GUIContent s_ArmForRecordDisabled;
  40. readonly InfiniteTrackDrawer m_InfiniteTrackDrawer;
  41. readonly TrackEditor m_TrackEditor;
  42. readonly GUIContent m_DefaultTrackIcon;
  43. readonly TrackResizeHandle m_ResizeHandle;
  44. TrackItemsDrawer m_ItemsDrawer;
  45. TrackDrawData m_TrackDrawData;
  46. TrackDrawOptions m_TrackDrawOptions;
  47. bool m_InlineCurvesSkipped;
  48. int m_TrackHash = -1;
  49. int m_BlendHash = -1;
  50. int m_LastDirtyIndex = -1;
  51. bool? m_TrackHasAnimatableParameters;
  52. int m_HeightExtension;
  53. public override bool expandable
  54. {
  55. get { return hasChildren; }
  56. }
  57. internal InlineCurveEditor inlineCurveEditor { get; set; }
  58. public ClipCurveEditor clipCurveEditor { get; private set; }
  59. public bool inlineCurvesSelected => SelectionManager.IsCurveEditorFocused(this);
  60. bool IClipCurveEditorOwner.showLoops
  61. {
  62. get { return false; }
  63. }
  64. TrackAsset IClipCurveEditorOwner.owner
  65. {
  66. get { return track; }
  67. }
  68. static bool DoesTrackAllowsRecording(TrackAsset track)
  69. {
  70. // if the root animation track is in auto mode, recording is not allowed
  71. var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
  72. if (animTrack != null)
  73. return animTrack.trackOffset != TrackOffset.Auto;
  74. return false;
  75. }
  76. bool trackHasAnimatableParameters
  77. {
  78. get
  79. {
  80. // cache this value to avoid the recomputation
  81. if (!m_TrackHasAnimatableParameters.HasValue)
  82. m_TrackHasAnimatableParameters = track.HasAnyAnimatableParameters() ||
  83. track.clips.Any(c => c.HasAnyAnimatableParameters());
  84. return m_TrackHasAnimatableParameters.Value;
  85. }
  86. }
  87. public bool locked
  88. {
  89. get { return track.lockedInHierarchy; }
  90. }
  91. public bool showMarkers
  92. {
  93. get { return track.GetShowMarkers(); }
  94. }
  95. public bool muted
  96. {
  97. get { return track.muted; }
  98. }
  99. public List<TimelineClipGUI> clips
  100. {
  101. get
  102. {
  103. return m_ItemsDrawer.clips == null ? new List<TimelineClipGUI>(0) : m_ItemsDrawer.clips;
  104. }
  105. }
  106. TrackAsset IRowGUI.asset { get { return track; } }
  107. bool showTrackRecordingDisabled
  108. {
  109. get
  110. {
  111. // if the root animation track is in auto mode, recording is not allowed
  112. var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack;
  113. return animTrack != null && animTrack.trackOffset == TrackOffset.Auto;
  114. }
  115. }
  116. public int heightExtension
  117. {
  118. get => m_HeightExtension;
  119. set => m_HeightExtension = Math.Max(0, value);
  120. }
  121. float minimumHeight => m_TrackDrawOptions.minimumHeight <= 0.0f ? TrackEditor.DefaultTrackHeight : m_TrackDrawOptions.minimumHeight;
  122. public TimelineTrackGUI(TreeViewController tv, TimelineTreeViewGUI w, int id, int depth, TreeViewItem parent, string displayName, TrackAsset sequenceActor)
  123. : base(tv, w, id, depth, parent, displayName, sequenceActor, false)
  124. {
  125. var animationTrack = sequenceActor as AnimationTrack;
  126. if (animationTrack != null)
  127. m_InfiniteTrackDrawer = new InfiniteTrackDrawer(new AnimationTrackKeyDataSource(animationTrack));
  128. else if (sequenceActor.HasAnyAnimatableParameters() && !sequenceActor.clips.Any())
  129. m_InfiniteTrackDrawer = new InfiniteTrackDrawer(new TrackPropertyCurvesDataSource(sequenceActor));
  130. UpdateInfiniteClipEditor(w.TimelineWindow);
  131. var bindings = track.outputs.ToArray();
  132. m_TrackDrawData.m_HasBinding = bindings.Length > 0;
  133. if (m_TrackDrawData.m_HasBinding)
  134. m_TrackDrawData.m_Binding = bindings[0];
  135. m_TrackDrawData.m_IsSubTrack = IsSubTrack();
  136. m_TrackDrawData.m_AllowsRecording = DoesTrackAllowsRecording(sequenceActor);
  137. m_TrackDrawData.m_HasMarkers = track.GetMarkerCount() > 0;
  138. m_DefaultTrackIcon = TrackResourceCache.GetTrackIcon(track);
  139. m_TrackEditor = CustomTimelineEditorCache.GetTrackEditor(sequenceActor);
  140. try
  141. {
  142. m_TrackDrawOptions = m_TrackEditor.GetTrackOptions(track, null);
  143. }
  144. catch (Exception e)
  145. {
  146. Debug.LogException(e);
  147. m_TrackDrawOptions = CustomTimelineEditorCache.GetDefaultTrackEditor().GetTrackOptions(track, null);
  148. }
  149. m_TrackDrawOptions.errorText = null; // explicitly setting to null for an uninitialized state
  150. m_ResizeHandle = new TrackResizeHandle(this);
  151. heightExtension = TimelineWindowViewPrefs.GetTrackHeightExtension(track);
  152. RebuildGUICacheIfNecessary();
  153. }
  154. public override float GetVerticalSpacingBetweenTracks()
  155. {
  156. if (track != null && track.isSubTrack)
  157. return 1.0f; // subtracks have less of a gap than tracks
  158. return base.GetVerticalSpacingBetweenTracks();
  159. }
  160. void UpdateInfiniteClipEditor(TimelineWindow window)
  161. {
  162. if (clipCurveEditor != null || track == null || !ShouldShowInfiniteClipEditor())
  163. return;
  164. var dataSource = CurveDataSource.Create(this);
  165. clipCurveEditor = new ClipCurveEditor(dataSource, window, track);
  166. }
  167. void DetectTrackChanged()
  168. {
  169. if (Event.current.type == EventType.Layout)
  170. {
  171. // incremented when a track or it's clips changed
  172. if (m_LastDirtyIndex != track.DirtyIndex)
  173. {
  174. try
  175. {
  176. m_TrackEditor.OnTrackChanged(track);
  177. }
  178. catch (Exception e)
  179. {
  180. Debug.LogException(e);
  181. }
  182. m_LastDirtyIndex = track.DirtyIndex;
  183. }
  184. OnTrackChanged();
  185. }
  186. }
  187. // Called when the source track data, including it's clips have changed has changed.
  188. void OnTrackChanged()
  189. {
  190. // recompute blends if necessary
  191. int newBlendHash = BlendHash();
  192. if (m_BlendHash != newBlendHash)
  193. {
  194. UpdateClipOverlaps();
  195. m_BlendHash = newBlendHash;
  196. }
  197. RebuildGUICacheIfNecessary();
  198. }
  199. void UpdateDrawData(WindowState state)
  200. {
  201. if (Event.current.type == EventType.Layout)
  202. {
  203. m_TrackDrawData.m_ShowTrackBindings = false;
  204. m_TrackDrawData.m_TrackBinding = null;
  205. if (state.editSequence.director != null && showSceneReference)
  206. {
  207. m_TrackDrawData.m_ShowTrackBindings = state.GetWindow().currentMode.ShouldShowTrackBindings(state);
  208. m_TrackDrawData.m_TrackBinding = state.editSequence.director.GetGenericBinding(track);
  209. }
  210. var lastHeight = m_TrackDrawOptions.minimumHeight;
  211. try
  212. {
  213. m_TrackDrawOptions = m_TrackEditor.GetTrackOptions(track, m_TrackDrawData.m_TrackBinding);
  214. }
  215. catch (Exception e)
  216. {
  217. Debug.LogException(e);
  218. m_TrackDrawOptions = CustomTimelineEditorCache.GetDefaultTrackEditor().GetTrackOptions(track, m_TrackDrawData.m_TrackBinding);
  219. }
  220. m_TrackDrawData.m_HasMarkers = track.GetMarkerCount() > 0;
  221. m_TrackDrawData.m_AllowsRecording = DoesTrackAllowsRecording(track);
  222. m_TrackDrawData.m_TrackIcon = m_TrackDrawOptions.icon;
  223. if (m_TrackDrawData.m_TrackIcon == null)
  224. m_TrackDrawData.m_TrackIcon = m_DefaultTrackIcon.image;
  225. // track height has changed. need to update gui
  226. if (!Mathf.Approximately(lastHeight, m_TrackDrawOptions.minimumHeight))
  227. state.Refresh();
  228. }
  229. }
  230. public override void Draw(Rect headerRect, Rect contentRect, WindowState state)
  231. {
  232. DetectTrackChanged();
  233. UpdateDrawData(state);
  234. UpdateInfiniteClipEditor(state.GetWindow());
  235. var trackHeaderRect = headerRect;
  236. var trackContentRect = contentRect;
  237. float inlineCurveHeight = contentRect.height - GetTrackContentHeight(state);
  238. bool hasInlineCurve = inlineCurveHeight > 0.0f;
  239. if (hasInlineCurve)
  240. {
  241. trackHeaderRect.height -= inlineCurveHeight;
  242. trackContentRect.height -= inlineCurveHeight;
  243. }
  244. if (Event.current.type == EventType.Repaint)
  245. {
  246. m_TreeViewRect = trackContentRect;
  247. }
  248. track.SetCollapsed(!isExpanded);
  249. RebuildGUICacheIfNecessary();
  250. // Prevents from drawing outside of bounds, but does not effect layout or markers
  251. bool isOwnerDrawSucceed = false;
  252. Vector2 visibleTime = state.timeAreaShownRange;
  253. if (drawer != null)
  254. isOwnerDrawSucceed = drawer.DrawTrack(trackContentRect, track, visibleTime, state);
  255. if (!isOwnerDrawSucceed)
  256. {
  257. using (new GUIViewportScope(trackContentRect))
  258. DrawBackground(trackContentRect, track, visibleTime, state);
  259. if (m_InfiniteTrackDrawer != null)
  260. m_InfiniteTrackDrawer.DrawTrack(trackContentRect, track, visibleTime, state);
  261. // draw after user customization so overlay text shows up
  262. using (new GUIViewportScope(trackContentRect))
  263. m_ItemsDrawer.Draw(trackContentRect, state);
  264. }
  265. DrawTrackHeader(trackHeaderRect, state);
  266. if (hasInlineCurve)
  267. {
  268. var curvesHeaderRect = headerRect;
  269. curvesHeaderRect.yMin = trackHeaderRect.yMax;
  270. var curvesContentRect = contentRect;
  271. curvesContentRect.yMin = trackContentRect.yMax;
  272. DrawInlineCurves(curvesHeaderRect, curvesContentRect, state);
  273. }
  274. DrawTrackColorKind(headerRect);
  275. DrawTrackState(contentRect, contentRect, track);
  276. }
  277. void DrawInlineCurves(Rect curvesHeaderRect, Rect curvesContentRect, WindowState state)
  278. {
  279. if (!track.GetShowInlineCurves())
  280. return;
  281. // Inline curves are not within the editor window -- case 952571
  282. if (!IsInlineCurvesEditorInBounds(ToWindowSpace(curvesHeaderRect), curvesContentRect.height, state))
  283. {
  284. m_InlineCurvesSkipped = true;
  285. return;
  286. }
  287. // If inline curves were skipped during the last event; we want to avoid rendering them until
  288. // the next Layout event. Otherwise, we still get the RTE prevented above when the user resizes
  289. // the timeline window very fast. -- case 952571
  290. if (m_InlineCurvesSkipped && Event.current.type != EventType.Layout)
  291. return;
  292. m_InlineCurvesSkipped = false;
  293. if (inlineCurveEditor == null)
  294. inlineCurveEditor = new InlineCurveEditor(this);
  295. curvesHeaderRect.x += DirectorStyles.kBaseIndent;
  296. curvesHeaderRect.width -= DirectorStyles.kBaseIndent;
  297. inlineCurveEditor.Draw(curvesHeaderRect, curvesContentRect, state);
  298. }
  299. static bool IsInlineCurvesEditorInBounds(Rect windowSpaceTrackRect, float inlineCurveHeight, WindowState state)
  300. {
  301. var legalHeight = state.windowHeight;
  302. var trackTop = windowSpaceTrackRect.y;
  303. var inlineCurveOffset = windowSpaceTrackRect.height - inlineCurveHeight;
  304. return legalHeight - trackTop - inlineCurveOffset > 0;
  305. }
  306. void DrawErrorIcon(Rect position, WindowState state)
  307. {
  308. Rect bindingLabel = position;
  309. bindingLabel.x = position.xMax + 3;
  310. bindingLabel.width = state.bindingAreaWidth;
  311. EditorGUI.LabelField(position, m_ProblemIcon);
  312. }
  313. void DrawBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state)
  314. {
  315. bool canDrawRecordBackground = IsRecording(state);
  316. if (canDrawRecordBackground)
  317. {
  318. DrawRecordingTrackBackground(trackRect, trackAsset, visibleTime, state);
  319. }
  320. else
  321. {
  322. Color trackBackgroundColor;
  323. if (SelectionManager.Contains(track))
  324. {
  325. trackBackgroundColor = state.IsEditingASubTimeline() ?
  326. DirectorStyles.Instance.customSkin.colorTrackSubSequenceBackgroundSelected :
  327. DirectorStyles.Instance.customSkin.colorTrackBackgroundSelected;
  328. }
  329. else
  330. {
  331. trackBackgroundColor = state.IsEditingASubTimeline() ?
  332. DirectorStyles.Instance.customSkin.colorTrackSubSequenceBackground :
  333. DirectorStyles.Instance.customSkin.colorTrackBackground;
  334. }
  335. EditorGUI.DrawRect(trackRect, trackBackgroundColor);
  336. }
  337. }
  338. float InlineCurveHeight()
  339. {
  340. return track.GetShowInlineCurves() && CanDrawInlineCurve()
  341. ? TimelineWindowViewPrefs.GetInlineCurveHeight(track)
  342. : 0.0f;
  343. }
  344. public override float GetHeight(WindowState state)
  345. {
  346. var height = GetTrackContentHeight(state);
  347. if (CanDrawInlineCurve())
  348. height += InlineCurveHeight();
  349. return height;
  350. }
  351. float GetTrackContentHeight(WindowState state)
  352. {
  353. var defaultHeight = Mathf.Min(minimumHeight, TrackEditor.MaximumTrackHeight);
  354. return (defaultHeight + heightExtension) * state.trackScale;
  355. }
  356. static bool CanDrawIcon(GUIContent icon)
  357. {
  358. return icon != null && icon != GUIContent.none && icon.image != null;
  359. }
  360. bool showSceneReference
  361. {
  362. get
  363. {
  364. return track != null &&
  365. m_TrackDrawData.m_HasBinding &&
  366. !m_TrackDrawData.m_IsSubTrack &&
  367. m_TrackDrawData.m_Binding.sourceObject != null &&
  368. m_TrackDrawData.m_Binding.outputTargetType != null &&
  369. typeof(Object).IsAssignableFrom(m_TrackDrawData.m_Binding.outputTargetType);
  370. }
  371. }
  372. void DrawTrackHeader(Rect trackHeaderRect, WindowState state)
  373. {
  374. using (new GUIViewportScope(trackHeaderRect))
  375. {
  376. var rect = trackHeaderRect;
  377. DrawHeaderBackground(trackHeaderRect);
  378. rect.x += m_Styles.trackSwatchStyle.fixedWidth;
  379. const float buttonSize = WindowConstants.trackHeaderButtonSize;
  380. const float padding = WindowConstants.trackHeaderButtonPadding;
  381. var buttonRect = new Rect(trackHeaderRect.xMax - buttonSize - padding, rect.y + (rect.height - buttonSize) / 2f, buttonSize, buttonSize);
  382. rect.x += DrawTrackIconKind(rect, state);
  383. if (track is GroupTrack)
  384. return;
  385. buttonRect.x -= DrawTrackDropDownMenu(buttonRect);
  386. var suiteRect = DrawGeneralSuite(state, buttonRect);
  387. suiteRect = DrawCustomSuite(state, suiteRect);
  388. var bindingRect = new Rect(rect.x, rect.y, suiteRect.xMax - rect.x, rect.height);
  389. DrawTrackBinding(bindingRect, trackHeaderRect);
  390. }
  391. m_ResizeHandle.Draw(trackHeaderRect, state);
  392. }
  393. Rect DrawGeneralSuite(WindowState state, Rect rect)
  394. {
  395. const float buttonWidth = WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
  396. var padding = DrawButtonSuite(3, ref rect);
  397. DrawMuteButton(rect, state);
  398. rect.x -= buttonWidth;
  399. DrawLockButton(rect, state);
  400. rect.x -= buttonWidth;
  401. DrawLockMarkersButton(rect, state);
  402. rect.x -= buttonWidth;
  403. rect.x -= padding;
  404. return rect;
  405. }
  406. Rect DrawCustomSuite(WindowState state, Rect rect)
  407. {
  408. var numberOfButtons = 0;
  409. if (m_TrackDrawData.m_AllowsRecording || showTrackRecordingDisabled)
  410. numberOfButtons++;
  411. if (CanDrawInlineCurve())
  412. numberOfButtons++;
  413. if (drawer.HasCustomTrackHeaderButton())
  414. numberOfButtons++;
  415. if (numberOfButtons == 0)
  416. return rect;
  417. var padding = DrawButtonSuite(numberOfButtons, ref rect);
  418. rect.x -= DrawRecordButton(rect, state);
  419. rect.x -= DrawInlineCurveButton(rect, state);
  420. rect.x -= DrawCustomTrackButton(rect, state);
  421. rect.x -= padding;
  422. return rect;
  423. }
  424. void DrawHeaderBackground(Rect headerRect)
  425. {
  426. Color backgroundColor = SelectionManager.Contains(track)
  427. ? DirectorStyles.Instance.customSkin.colorSelection
  428. : DirectorStyles.Instance.customSkin.colorTrackHeaderBackground;
  429. var bgRect = headerRect;
  430. bgRect.x += m_Styles.trackSwatchStyle.fixedWidth;
  431. bgRect.width -= m_Styles.trackSwatchStyle.fixedWidth;
  432. EditorGUI.DrawRect(bgRect, backgroundColor);
  433. }
  434. void DrawTrackColorKind(Rect rect)
  435. {
  436. // subtracks don't draw the color, the parent does that.
  437. if (track != null && track.isSubTrack)
  438. return;
  439. if (rect.width <= 0) return;
  440. using (new GUIColorOverride(m_TrackDrawOptions.trackColor))
  441. {
  442. rect.width = m_Styles.trackSwatchStyle.fixedWidth;
  443. GUI.Label(rect, GUIContent.none, m_Styles.trackSwatchStyle);
  444. }
  445. }
  446. float DrawTrackIconKind(Rect rect, WindowState state)
  447. {
  448. // no icons on subtracks
  449. if (track != null && track.isSubTrack)
  450. return 0.0f;
  451. rect.yMin += (rect.height - 16f) / 2f;
  452. rect.width = 16.0f;
  453. rect.height = 16.0f;
  454. if (!string.IsNullOrEmpty(m_TrackDrawOptions.errorText))
  455. {
  456. m_ProblemIcon.image = Styles.kProblemIcon;
  457. m_ProblemIcon.tooltip = m_TrackDrawOptions.errorText;
  458. if (CanDrawIcon(m_ProblemIcon))
  459. DrawErrorIcon(rect, state);
  460. }
  461. else
  462. {
  463. var content = GUIContent.Temp(m_TrackDrawData.m_TrackIcon, m_DefaultTrackIcon.tooltip);
  464. if (CanDrawIcon(content))
  465. GUI.Box(rect, content, GUIStyle.none);
  466. }
  467. return rect.width;
  468. }
  469. void DrawTrackBinding(Rect rect, Rect headerRect)
  470. {
  471. if (m_TrackDrawData.m_ShowTrackBindings)
  472. {
  473. DoTrackBindingGUI(rect);
  474. return;
  475. }
  476. var textStyle = m_Styles.trackHeaderFont;
  477. textStyle.normal.textColor = SelectionManager.Contains(track) ? Color.white : m_Styles.customSkin.colorTrackFont;
  478. string trackName = track.name;
  479. EditorGUI.BeginChangeCheck();
  480. // by default the size is just the width of the string (for selection purposes)
  481. rect.width = m_Styles.trackHeaderFont.CalcSize(new GUIContent(trackName)).x;
  482. // if we are editing, supply the entire width of the header
  483. if (GUIUtility.keyboardControl == track.GetInstanceID())
  484. rect.width = (headerRect.xMax - rect.xMin) - (5 * WindowConstants.trackHeaderButtonSize);
  485. trackName = EditorGUI.DelayedTextField(rect, GUIContent.none, track.GetInstanceID(), track.name, textStyle);
  486. if (EditorGUI.EndChangeCheck())
  487. {
  488. UndoExtensions.RegisterTrack(track, "Rename Track");
  489. track.name = trackName;
  490. }
  491. }
  492. float DrawTrackDropDownMenu(Rect rect)
  493. {
  494. if (GUI.Button(rect, GUIContent.none, m_Styles.trackOptions))
  495. {
  496. // the drop down will apply to all selected tracks
  497. if (!SelectionManager.Contains(track))
  498. {
  499. SelectionManager.Clear();
  500. SelectionManager.Add(track);
  501. }
  502. SequencerContextMenu.ShowTrackContextMenu(null);
  503. }
  504. return WindowConstants.trackHeaderButtonSize;
  505. }
  506. bool CanDrawInlineCurve()
  507. {
  508. // Note: A track with animatable parameters always has inline curves.
  509. return trackHasAnimatableParameters || TimelineUtility.TrackHasAnimationCurves(track);
  510. }
  511. float DrawInlineCurveButton(Rect rect, WindowState state)
  512. {
  513. if (!CanDrawInlineCurve())
  514. {
  515. //Force to close Inline Curve UI if the inline cannot be drawn.
  516. if (track.GetShowInlineCurves())
  517. track.SetShowInlineCurves(false);
  518. return 0.0f;
  519. }
  520. // Override enable state to display "Show Inline Curves" button in disabled state.
  521. bool prevEnabledState = GUI.enabled;
  522. GUI.enabled = true;
  523. var showInlineCurves = track.GetShowInlineCurves();
  524. var tooltip = showInlineCurves ? Styles.trackCurvesBtnOnTooltip : Styles.trackCurvesBtnOffTooltip;
  525. var newValue = GUI.Toggle(rect, track.GetShowInlineCurves(), tooltip, DirectorStyles.Instance.trackCurvesButton);
  526. GUI.enabled = prevEnabledState;
  527. if (newValue != track.GetShowInlineCurves())
  528. {
  529. track.SetShowInlineCurves(newValue);
  530. state.GetWindow().treeView.CalculateRowRects();
  531. }
  532. return WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
  533. }
  534. float DrawRecordButton(Rect rect, WindowState state)
  535. {
  536. var style = DirectorStyles.Instance.trackRecordButton;
  537. const float buttonWidth = WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
  538. if (m_TrackDrawData.m_AllowsRecording)
  539. {
  540. bool isPlayerDisabled = state.editSequence.director != null && !state.editSequence.director.isActiveAndEnabled;
  541. GameObject goBinding = m_TrackDrawData.m_TrackBinding as GameObject;
  542. if (goBinding == null)
  543. {
  544. Component c = m_TrackDrawData.m_TrackBinding as Component;
  545. if (c != null)
  546. goBinding = c.gameObject;
  547. }
  548. if (goBinding == null && m_TrackDrawData.m_IsSubTrack)
  549. goBinding = ParentTrack().GetGameObjectBinding(state.editSequence.director);
  550. var isTrackBindingValid = goBinding != null;
  551. var trackErrorDisableButton = !string.IsNullOrEmpty(m_TrackDrawOptions.errorText) && isTrackBindingValid && goBinding.activeInHierarchy;
  552. var disableButton = track.lockedInHierarchy || isPlayerDisabled || trackErrorDisableButton || !isTrackBindingValid || state.ignorePreview;
  553. using (new EditorGUI.DisabledScope(disableButton))
  554. {
  555. if (IsRecording(state))
  556. {
  557. state.editorWindow.Repaint();
  558. var remainder = Time.realtimeSinceStartup % 1;
  559. if (remainder < 0.22f)
  560. style = GUIStyle.none;
  561. if (GUI.Button(rect, Styles.kActiveRecordButtonTooltip, style) || isPlayerDisabled || !isTrackBindingValid)
  562. state.UnarmForRecord(track);
  563. }
  564. else if (!track.timelineAsset.editorSettings.scenePreview)
  565. GUI.Button(rect, Styles.kIgnorePreviewRecordButtonTooltip, style);
  566. else
  567. {
  568. if (GUI.Button(rect, Styles.kInactiveRecordButtonTooltip, style))
  569. state.ArmForRecord(track);
  570. }
  571. return buttonWidth;
  572. }
  573. }
  574. if (showTrackRecordingDisabled)
  575. {
  576. using (new EditorGUI.DisabledScope(true))
  577. GUI.Button(rect, Styles.kDisabledRecordButtonTooltip, style);
  578. return buttonWidth;
  579. }
  580. return 0.0f;
  581. }
  582. float DrawCustomTrackButton(Rect rect, WindowState state)
  583. {
  584. if (!drawer.HasCustomTrackHeaderButton())
  585. return 0.0f;
  586. drawer.DrawTrackHeaderButton(rect, state);
  587. return WindowConstants.trackHeaderButtonSize + WindowConstants.trackHeaderButtonPadding;
  588. }
  589. void DrawLockMarkersButton(Rect rect, WindowState state)
  590. {
  591. var hasMarkers = track.GetMarkerCount() != 0;
  592. var markersShown = showMarkers && hasMarkers;
  593. var style = TimelineWindow.styles.trackMarkerButton;
  594. EditorGUI.BeginChangeCheck();
  595. var tooltip = markersShown ? Styles.trackMarkerBtnOnTooltip : Styles.trackMarkerBtnOffTooltip;
  596. var toggleMarkers = GUI.Toggle(rect, markersShown, tooltip, style);
  597. if (EditorGUI.EndChangeCheck() && hasMarkers)
  598. track.SetShowTrackMarkers(toggleMarkers);
  599. }
  600. static void ObjectBindingField(Rect position, Object obj, PlayableBinding binding)
  601. {
  602. var allowScene =
  603. typeof(GameObject).IsAssignableFrom(binding.outputTargetType) ||
  604. typeof(Component).IsAssignableFrom(binding.outputTargetType);
  605. var bindingFieldRect = EditorGUI.IndentedRect(position);
  606. using (new GUIViewportScope(bindingFieldRect))
  607. {
  608. using (var check = new EditorGUI.ChangeCheckScope())
  609. {
  610. // FocusType.Passive so it never gets focused when pressing tab
  611. int controlId = GUIUtility.GetControlID("s_ObjectFieldHash".GetHashCode(), FocusType.Passive, position);
  612. var newObject = UnityEditorInternals.DoObjectField(EditorGUI.IndentedRect(position), obj, binding.outputTargetType, controlId, allowScene);
  613. if (check.changed)
  614. {
  615. BindingUtility.Bind(TimelineEditor.inspectedDirector, binding.sourceObject as TrackAsset, newObject);
  616. }
  617. }
  618. }
  619. }
  620. void DoTrackBindingGUI(Rect rect)
  621. {
  622. var bindingRect = new Rect(
  623. rect.xMin,
  624. rect.y + (rect.height - WindowConstants.trackHeaderButtonSize) / 2f,
  625. Mathf.Min(rect.width, WindowConstants.trackBindingMaxSize) - WindowConstants.trackBindingPadding,
  626. WindowConstants.trackHeaderButtonSize);
  627. if (bindingRect.Contains(Event.current.mousePosition) && TimelineDragging.IsDraggingEvent() && DragAndDrop.objectReferences.Length == 1)
  628. {
  629. TimelineDragging.HandleBindingDragAndDrop(track, BindingUtility.GetRequiredBindingType(m_TrackDrawData.m_Binding));
  630. Event.current.Use();
  631. }
  632. else
  633. {
  634. if (m_TrackDrawData.m_Binding.outputTargetType != null && typeof(Object).IsAssignableFrom(m_TrackDrawData.m_Binding.outputTargetType))
  635. {
  636. ObjectBindingField(bindingRect, m_TrackDrawData.m_TrackBinding, m_TrackDrawData.m_Binding);
  637. }
  638. }
  639. }
  640. bool IsRecording(WindowState state)
  641. {
  642. return state.recording && state.IsArmedForRecord(track);
  643. }
  644. // background to draw during recording
  645. void DrawRecordingTrackBackground(Rect trackRect, TrackAsset trackAsset, Vector2 visibleTime, WindowState state)
  646. {
  647. if (drawer != null)
  648. drawer.DrawRecordingBackground(trackRect, trackAsset, visibleTime, state);
  649. }
  650. void UpdateClipOverlaps()
  651. {
  652. TrackExtensions.ComputeBlendsFromOverlaps(track.clips);
  653. }
  654. internal void RebuildGUICacheIfNecessary()
  655. {
  656. if (m_TrackHash == track.Hash())
  657. return;
  658. m_ItemsDrawer = new TrackItemsDrawer(this);
  659. m_TrackHash = track.Hash();
  660. }
  661. int BlendHash()
  662. {
  663. var hash = 0;
  664. foreach (var clip in track.clips)
  665. {
  666. hash = HashUtility.CombineHash(hash,
  667. (clip.duration - clip.start).GetHashCode(),
  668. ((int)clip.blendInCurveMode).GetHashCode(),
  669. ((int)clip.blendOutCurveMode).GetHashCode());
  670. }
  671. return hash;
  672. }
  673. // callback when the corresponding graph is rebuilt. This can happen, but not have the GUI rebuilt.
  674. public override void OnGraphRebuilt()
  675. {
  676. RefreshCurveEditor();
  677. }
  678. void RefreshCurveEditor()
  679. {
  680. var window = TimelineWindow.instance;
  681. if (track != null && window != null && window.state != null)
  682. {
  683. bool hasEditor = clipCurveEditor != null;
  684. bool shouldHaveEditor = ShouldShowInfiniteClipEditor();
  685. if (hasEditor != shouldHaveEditor)
  686. window.state.AddEndFrameDelegate((x, currentEvent) =>
  687. {
  688. x.Refresh();
  689. return true;
  690. });
  691. }
  692. }
  693. bool ShouldShowInfiniteClipEditor()
  694. {
  695. var animationTrack = track as AnimationTrack;
  696. if (animationTrack != null)
  697. return animationTrack.ShouldShowInfiniteClipEditor();
  698. return trackHasAnimatableParameters;
  699. }
  700. public void SelectCurves()
  701. {
  702. SelectionManager.RemoveTimelineSelection();
  703. SelectionManager.SelectInlineCurveEditor(this);
  704. }
  705. public void ValidateCurvesSelection() {}
  706. }
  707. }