231 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| 
 | |
| #if UNITY_2021_2_OR_NEWER
 | |
| using UnityEditor.SceneManagement;
 | |
| #else
 | |
| using UnityEditor.Experimental.SceneManagement;
 | |
| #endif
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace UnityEditor.Timeline
 | |
| {
 | |
|     enum TitleMode
 | |
|     {
 | |
|         None,
 | |
|         DisabledComponent,
 | |
|         Prefab,
 | |
|         PrefabOutOfContext,
 | |
|         Asset,
 | |
|         GameObject
 | |
|     }
 | |
| 
 | |
|     struct BreadCrumbTitle
 | |
|     {
 | |
|         public string name;
 | |
|         public TitleMode mode;
 | |
|     }
 | |
| 
 | |
|     class BreadcrumbDrawer
 | |
|     {
 | |
|         static readonly GUIContent s_TextContent = new GUIContent();
 | |
|         static readonly string k_DisabledComponentText = L10n.Tr("The PlayableDirector is disabled");
 | |
|         static readonly string k_PrefabOutOfContext = L10n.Tr("Prefab Isolation not enabled. Click to Enable.");
 | |
| 
 | |
|         static readonly GUIStyle k_BreadCrumbLeft;
 | |
|         static readonly GUIStyle k_BreadCrumbMid;
 | |
|         static readonly GUIStyle k_BreadCrumbLeftBg;
 | |
|         static readonly GUIStyle k_BreadCrumbMidBg;
 | |
|         static readonly GUIStyle k_BreadCrumbMidSelected;
 | |
|         static readonly GUIStyle k_BreadCrumbMidBgSelected;
 | |
| 
 | |
|         static readonly Texture k_TimelineIcon;
 | |
| 
 | |
|         const string k_Elipsis = "…";
 | |
| 
 | |
|         static BreadcrumbDrawer()
 | |
|         {
 | |
|             k_BreadCrumbLeft = "GUIEditor.BreadcrumbLeft";
 | |
|             k_BreadCrumbMid = "GUIEditor.BreadcrumbMid";
 | |
|             k_BreadCrumbLeftBg = "GUIEditor.BreadcrumbLeftBackground";
 | |
|             k_BreadCrumbMidBg = "GUIEditor.BreadcrumbMidBackground";
 | |
| 
 | |
|             k_BreadCrumbMidSelected = k_BreadCrumbMid;
 | |
|             k_BreadCrumbMidSelected.normal = k_BreadCrumbMidSelected.onNormal;
 | |
| 
 | |
|             k_BreadCrumbMidBgSelected = k_BreadCrumbMidBg;
 | |
|             k_BreadCrumbMidBgSelected.normal = k_BreadCrumbMidBgSelected.onNormal;
 | |
|             k_TimelineIcon = EditorGUIUtility.IconContent("TimelineAsset Icon").image;
 | |
|         }
 | |
| 
 | |
|         static string FitTextInArea(float areaWidth, string text, GUIStyle style)
 | |
|         {
 | |
|             var borderWidth = style.border.left + style.border.right;
 | |
|             var textWidth = style.CalcSize(EditorGUIUtility.TextContent(text)).x;
 | |
| 
 | |
|             if (borderWidth + textWidth < areaWidth)
 | |
|                 return text;
 | |
| 
 | |
|             // Need to truncate the text to fit in the areaWidth
 | |
|             var textAreaWidth = areaWidth - borderWidth;
 | |
|             var pixByChar = textWidth / text.Length;
 | |
|             var charNeeded = (int)Mathf.Floor(textAreaWidth / pixByChar);
 | |
|             charNeeded -= k_Elipsis.Length;
 | |
| 
 | |
|             if (charNeeded <= 0)
 | |
|                 return k_Elipsis;
 | |
| 
 | |
|             if (charNeeded <= text.Length)
 | |
|                 return k_Elipsis + " " + text.Substring(text.Length - charNeeded);
 | |
| 
 | |
|             return k_Elipsis;
 | |
|         }
 | |
| 
 | |
|         public static void Draw(float breadcrumbAreaWidth, List<BreadCrumbTitle> labels, Action<int> navigateToBreadcrumbIndex)
 | |
|         {
 | |
|             GUILayout.BeginHorizontal();
 | |
|             {
 | |
|                 var labelWidth = (int)(breadcrumbAreaWidth / labels.Count);
 | |
| 
 | |
|                 for (var i = 0; i < labels.Count; i++)
 | |
|                 {
 | |
|                     var label = labels[i];
 | |
| 
 | |
|                     var style = i == 0 ? k_BreadCrumbLeft : k_BreadCrumbMid;
 | |
|                     var backgroundStyle = i == 0 ? k_BreadCrumbLeftBg : k_BreadCrumbMidBg;
 | |
| 
 | |
|                     if (i == labels.Count - 1)
 | |
|                     {
 | |
|                         if (i > 0) // Only tint last breadcrumb if we are dug-in
 | |
|                             DrawBreadcrumbAsSelectedSubSequence(labelWidth, label, k_BreadCrumbMidSelected, k_BreadCrumbMidBgSelected);
 | |
|                         else
 | |
|                             DrawActiveBreadcrumb(labelWidth, label, style, backgroundStyle);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         var previousContentColor = GUI.contentColor;
 | |
|                         GUI.contentColor = new Color(previousContentColor.r,
 | |
|                             previousContentColor.g,
 | |
|                             previousContentColor.b,
 | |
|                             previousContentColor.a * 0.6f);
 | |
|                         var content = GetTextContent(labelWidth, label, style);
 | |
|                         var rect = GetBreadcrumbLayoutRect(content, style);
 | |
| 
 | |
|                         if (Event.current.type == EventType.Repaint)
 | |
|                             backgroundStyle.Draw(rect, GUIContent.none, 0);
 | |
| 
 | |
|                         if (GUI.Button(rect, content, style))
 | |
|                             navigateToBreadcrumbIndex.Invoke(i);
 | |
|                         GUI.contentColor = previousContentColor;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             GUILayout.EndHorizontal();
 | |
|         }
 | |
| 
 | |
|         static GUIContent GetTextContent(int width, BreadCrumbTitle text, GUIStyle style)
 | |
|         {
 | |
|             s_TextContent.tooltip = string.Empty;
 | |
|             s_TextContent.image = null;
 | |
|             if (text.mode == TitleMode.DisabledComponent)
 | |
|             {
 | |
|                 s_TextContent.tooltip = k_DisabledComponentText;
 | |
|                 s_TextContent.image = EditorGUIUtility.GetHelpIcon(MessageType.Warning);
 | |
|             }
 | |
|             else if (text.mode == TitleMode.Prefab)
 | |
|                 s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
 | |
|             else if (text.mode == TitleMode.GameObject)
 | |
|                 s_TextContent.image = PrefabUtility.GameObjectStyles.gameObjectIcon;
 | |
|             else if (text.mode == TitleMode.Asset)
 | |
|                 s_TextContent.image = k_TimelineIcon;
 | |
|             else if (text.mode == TitleMode.PrefabOutOfContext)
 | |
|             {
 | |
|                 s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
 | |
|                 if (!TimelineWindow.instance.locked)
 | |
|                     s_TextContent.tooltip = k_PrefabOutOfContext;
 | |
|             }
 | |
| 
 | |
|             if (s_TextContent.image != null)
 | |
|                 width = Math.Max(0, width - s_TextContent.image.width);
 | |
|             s_TextContent.text = FitTextInArea(width, text.name, style);
 | |
| 
 | |
|             return s_TextContent;
 | |
|         }
 | |
| 
 | |
|         static void DrawBreadcrumbAsSelectedSubSequence(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
 | |
|         {
 | |
|             var rect = DrawActiveBreadcrumb(width, label, style, backgroundStyle);
 | |
|             const float underlineThickness = 2.0f;
 | |
|             const float underlineVerticalOffset = 0.0f;
 | |
|             var underlineHorizontalOffset = backgroundStyle.border.right * 0.333f;
 | |
|             var underlineRect = Rect.MinMaxRect(
 | |
|                 rect.xMin - underlineHorizontalOffset,
 | |
|                 rect.yMax - underlineThickness - underlineVerticalOffset,
 | |
|                 rect.xMax - underlineHorizontalOffset,
 | |
|                 rect.yMax - underlineVerticalOffset);
 | |
| 
 | |
|             EditorGUI.DrawRect(underlineRect, DirectorStyles.Instance.customSkin.colorSubSequenceDurationLine);
 | |
|         }
 | |
| 
 | |
|         static Rect GetBreadcrumbLayoutRect(GUIContent content, GUIStyle style)
 | |
|         {
 | |
|             // the image makes the button far too big compared to non-image versions
 | |
|             var image = content.image;
 | |
|             content.image = null;
 | |
|             var size = style.CalcSizeWithConstraints(content, Vector2.zero);
 | |
|             content.image = image;
 | |
|             if (image != null)
 | |
|                 size.x += size.y; // assumes square image, constrained by height
 | |
| 
 | |
|             return GUILayoutUtility.GetRect(content, style, GUILayout.MaxWidth(size.x));
 | |
|         }
 | |
| 
 | |
|         static Rect DrawActiveBreadcrumb(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
 | |
|         {
 | |
|             var content = GetTextContent(width, label, style);
 | |
|             var rect = GetBreadcrumbLayoutRect(content, style);
 | |
| 
 | |
|             if (Event.current.type == EventType.Repaint)
 | |
|             {
 | |
|                 backgroundStyle.Draw(rect, GUIContent.none, 0);
 | |
|             }
 | |
| 
 | |
|             if (GUI.Button(rect, content, style))
 | |
|             {
 | |
|                 UnityEngine.Object target = TimelineEditor.inspectedDirector;
 | |
|                 if (target == null)
 | |
|                     target = TimelineEditor.inspectedAsset;
 | |
|                 if (target != null)
 | |
|                 {
 | |
|                     bool ping = true;
 | |
|                     if (label.mode == TitleMode.PrefabOutOfContext)
 | |
|                     {
 | |
|                         var gameObject = PrefabUtility.GetRootGameObject(target);
 | |
|                         if (gameObject != null)
 | |
|                         {
 | |
|                             target = gameObject;  // ping the prefab root if it's locked.
 | |
|                             if (!TimelineWindow.instance.locked)
 | |
|                             {
 | |
|                                 var assetPath = AssetDatabase.GetAssetPath(gameObject);
 | |
|                                 if (!string.IsNullOrEmpty(assetPath))
 | |
|                                 {
 | |
|                                     var stage = PrefabStageUtility.OpenPrefab(assetPath);
 | |
|                                     if (stage != null)
 | |
|                                         ping = false;
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     if (ping)
 | |
|                     {
 | |
|                         EditorGUIUtility.PingObject(target);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return rect;
 | |
|         }
 | |
|     }
 | |
| }
 |