No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TimelineTrackGUI.cs 32KB

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