BreadcrumbDrawer.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Timeline;
  5. namespace UnityEditor.Timeline
  6. {
  7. enum TitleMode
  8. {
  9. None,
  10. DisabledComponent,
  11. Prefab,
  12. PrefabOutOfContext,
  13. Asset,
  14. GameObject
  15. }
  16. struct BreadCrumbTitle
  17. {
  18. public string name;
  19. public TitleMode mode;
  20. }
  21. class BreadcrumbDrawer
  22. {
  23. static readonly GUIContent s_TextContent = new GUIContent();
  24. static readonly string k_DisabledComponentText = L10n.Tr("The PlayableDirector is disabled");
  25. static readonly string k_PrefabOutOfContext = L10n.Tr("Prefab Isolation not enabled. Click to Enable.");
  26. static readonly GUIStyle k_BreadCrumbLeft;
  27. static readonly GUIStyle k_BreadCrumbMid;
  28. static readonly GUIStyle k_BreadCrumbLeftBg;
  29. static readonly GUIStyle k_BreadCrumbMidBg;
  30. static readonly GUIStyle k_BreadCrumbMidSelected;
  31. static readonly GUIStyle k_BreadCrumbMidBgSelected;
  32. static readonly Texture k_TimelineIcon;
  33. const string k_Elipsis = "…";
  34. static BreadcrumbDrawer()
  35. {
  36. k_BreadCrumbLeft = new GUIStyle("GUIEditor.BreadcrumbLeft");
  37. k_BreadCrumbMid = new GUIStyle("GUIEditor.BreadcrumbMid");
  38. k_BreadCrumbLeftBg = new GUIStyle("GUIEditor.BreadcrumbLeftBackground");
  39. k_BreadCrumbMidBg = new GUIStyle("GUIEditor.BreadcrumbMidBackground");
  40. k_BreadCrumbMidSelected = new GUIStyle(k_BreadCrumbMid);
  41. k_BreadCrumbMidSelected.normal = k_BreadCrumbMidSelected.onNormal;
  42. k_BreadCrumbMidBgSelected = new GUIStyle(k_BreadCrumbMidBg);
  43. k_BreadCrumbMidBgSelected.normal = k_BreadCrumbMidBgSelected.onNormal;
  44. k_TimelineIcon = EditorGUIUtility.IconContent("TimelineAsset Icon").image;
  45. }
  46. static string FitTextInArea(float areaWidth, string text, GUIStyle style)
  47. {
  48. var borderWidth = style.border.left + style.border.right;
  49. var textWidth = style.CalcSize(EditorGUIUtility.TextContent(text)).x;
  50. if (borderWidth + textWidth < areaWidth)
  51. return text;
  52. // Need to truncate the text to fit in the areaWidth
  53. var textAreaWidth = areaWidth - borderWidth;
  54. var pixByChar = textWidth / text.Length;
  55. var charNeeded = (int)Mathf.Floor(textAreaWidth / pixByChar);
  56. charNeeded -= k_Elipsis.Length;
  57. if (charNeeded <= 0)
  58. return k_Elipsis;
  59. if (charNeeded <= text.Length)
  60. return k_Elipsis + " " + text.Substring(text.Length - charNeeded);
  61. return k_Elipsis;
  62. }
  63. public static void Draw(float breadcrumbAreaWidth, List<BreadCrumbTitle> labels, Action<int> navigateToBreadcrumbIndex)
  64. {
  65. GUILayout.BeginHorizontal(GUILayout.Width(breadcrumbAreaWidth));
  66. {
  67. var labelWidth = (int)(breadcrumbAreaWidth / labels.Count);
  68. for (var i = 0; i < labels.Count; i++)
  69. {
  70. var label = labels[i];
  71. var style = i == 0 ? k_BreadCrumbLeft : k_BreadCrumbMid;
  72. var backgroundStyle = i == 0 ? k_BreadCrumbLeftBg : k_BreadCrumbMidBg;
  73. if (i == labels.Count - 1)
  74. {
  75. if (i > 0)
  76. {
  77. // Only tint last breadcrumb if we are dug-in
  78. DrawBreadcrumbAsSelectedSubSequence(labelWidth, label, k_BreadCrumbMidSelected, k_BreadCrumbMidBgSelected);
  79. }
  80. else
  81. {
  82. DrawActiveBreadcrumb(labelWidth, label, style, backgroundStyle);
  83. }
  84. }
  85. else
  86. {
  87. var previousContentColor = GUI.contentColor;
  88. GUI.contentColor = new Color(previousContentColor.r,
  89. previousContentColor.g,
  90. previousContentColor.b,
  91. previousContentColor.a * 0.6f);
  92. var content = GetTextContent(labelWidth, label, style);
  93. var rect = GetBreadcrumbLayoutRect(content, style);
  94. if (Event.current.type == EventType.Repaint)
  95. {
  96. backgroundStyle.Draw(rect, GUIContent.none, 0);
  97. }
  98. if (GUI.Button(rect, content, style))
  99. {
  100. navigateToBreadcrumbIndex.Invoke(i);
  101. }
  102. GUI.contentColor = previousContentColor;
  103. }
  104. }
  105. }
  106. GUILayout.EndHorizontal();
  107. }
  108. static GUIContent GetTextContent(int width, BreadCrumbTitle text, GUIStyle style)
  109. {
  110. s_TextContent.tooltip = string.Empty;
  111. s_TextContent.image = null;
  112. if (text.mode == TitleMode.DisabledComponent)
  113. {
  114. s_TextContent.tooltip = k_DisabledComponentText;
  115. s_TextContent.image = EditorGUIUtility.GetHelpIcon(MessageType.Warning);
  116. }
  117. else if (text.mode == TitleMode.Prefab)
  118. s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
  119. else if (text.mode == TitleMode.GameObject)
  120. s_TextContent.image = PrefabUtility.GameObjectStyles.gameObjectIcon;
  121. else if (text.mode == TitleMode.Asset)
  122. s_TextContent.image = k_TimelineIcon;
  123. else if (text.mode == TitleMode.PrefabOutOfContext)
  124. {
  125. s_TextContent.image = PrefabUtility.GameObjectStyles.prefabIcon;
  126. if (!TimelineWindow.instance.locked)
  127. s_TextContent.tooltip = k_PrefabOutOfContext;
  128. }
  129. if (s_TextContent.image != null)
  130. width = Math.Max(0, width - s_TextContent.image.width);
  131. s_TextContent.text = FitTextInArea(width, text.name, style);
  132. return s_TextContent;
  133. }
  134. static void DrawBreadcrumbAsSelectedSubSequence(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
  135. {
  136. var rect = DrawActiveBreadcrumb(width, label, style, backgroundStyle);
  137. const float underlineThickness = 2.0f;
  138. const float underlineVerticalOffset = 0.0f;
  139. var underlineHorizontalOffset = backgroundStyle.border.right * 0.333f;
  140. var underlineRect = Rect.MinMaxRect(
  141. rect.xMin - underlineHorizontalOffset,
  142. rect.yMax - underlineThickness - underlineVerticalOffset,
  143. rect.xMax - underlineHorizontalOffset,
  144. rect.yMax - underlineVerticalOffset);
  145. EditorGUI.DrawRect(underlineRect, DirectorStyles.Instance.customSkin.colorSubSequenceDurationLine);
  146. }
  147. static Rect GetBreadcrumbLayoutRect(GUIContent content, GUIStyle style)
  148. {
  149. // the image makes the button far too big compared to non-image versions
  150. var image = content.image;
  151. content.image = null;
  152. var size = style.CalcSizeWithConstraints(content, Vector2.zero);
  153. content.image = image;
  154. if (image != null)
  155. size.x += size.y; // assumes square image, constrained by height
  156. return GUILayoutUtility.GetRect(content, style, GUILayout.MaxWidth(size.x));
  157. }
  158. static Rect DrawActiveBreadcrumb(int width, BreadCrumbTitle label, GUIStyle style, GUIStyle backgroundStyle)
  159. {
  160. var content = GetTextContent(width, label, style);
  161. var rect = GetBreadcrumbLayoutRect(content, style);
  162. if (Event.current.type == EventType.Repaint)
  163. {
  164. backgroundStyle.Draw(rect, GUIContent.none, 0);
  165. }
  166. if (GUI.Button(rect, content, style))
  167. {
  168. UnityEngine.Object target = TimelineEditor.inspectedDirector;
  169. if (target == null)
  170. target = TimelineEditor.inspectedAsset;
  171. if (target != null)
  172. {
  173. bool ping = true;
  174. if (label.mode == TitleMode.PrefabOutOfContext)
  175. {
  176. var gameObject = PrefabUtility.GetRootGameObject(target);
  177. if (gameObject != null)
  178. {
  179. target = gameObject; // ping the prefab root if it's locked.
  180. if (!TimelineWindow.instance.locked)
  181. {
  182. var assetPath = AssetDatabase.GetAssetPath(gameObject);
  183. if (!string.IsNullOrEmpty(assetPath))
  184. {
  185. var stage = Experimental.SceneManagement.PrefabStageUtility.OpenPrefab(assetPath);
  186. if (stage != null)
  187. ping = false;
  188. }
  189. }
  190. }
  191. }
  192. if (ping)
  193. {
  194. EditorGUIUtility.PingObject(target);
  195. }
  196. }
  197. }
  198. return rect;
  199. }
  200. }
  201. }