| 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; |
|
|
| |
| 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) |
| 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) |
| { |
| |
| var image = content.image; |
| content.image = null; |
| var size = style.CalcSizeWithConstraints(content, Vector2.zero); |
| content.image = image; |
| if (image != null) |
| size.x += size.y; |
|
|
| 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; |
| 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; |
| } |
| } |
| } |
|
|