TimelineTrackGUI.cs 30 KB

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