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