TimelineTrackGUI.cs 30 KB

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