DebugLogItem.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using UnityEngine.EventSystems;
  4. using System.Text;
  5. #if UNITY_EDITOR
  6. using UnityEditor;
  7. using System.Text.RegularExpressions;
  8. #endif
  9. // A UI element to show information about a debug entry
  10. namespace IngameDebugConsole
  11. {
  12. public class DebugLogItem : MonoBehaviour, IPointerClickHandler
  13. {
  14. #region Platform Specific Elements
  15. #if !UNITY_2018_1_OR_NEWER
  16. #if !UNITY_EDITOR && UNITY_ANDROID
  17. private static AndroidJavaClass m_ajc = null;
  18. private static AndroidJavaClass AJC
  19. {
  20. get
  21. {
  22. if( m_ajc == null )
  23. m_ajc = new AndroidJavaClass( "com.yasirkula.unity.DebugConsole" );
  24. return m_ajc;
  25. }
  26. }
  27. private static AndroidJavaObject m_context = null;
  28. private static AndroidJavaObject Context
  29. {
  30. get
  31. {
  32. if( m_context == null )
  33. {
  34. using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) )
  35. {
  36. m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" );
  37. }
  38. }
  39. return m_context;
  40. }
  41. }
  42. #elif !UNITY_EDITOR && UNITY_IOS
  43. [System.Runtime.InteropServices.DllImport( "__Internal" )]
  44. private static extern void _DebugConsole_CopyText( string text );
  45. #endif
  46. #endif
  47. #endregion
  48. #pragma warning disable 0649
  49. // Cached components
  50. [SerializeField]
  51. private RectTransform transformComponent;
  52. public RectTransform Transform { get { return transformComponent; } }
  53. [SerializeField]
  54. private Image imageComponent;
  55. public Image Image { get { return imageComponent; } }
  56. [SerializeField]
  57. private CanvasGroup canvasGroupComponent;
  58. public CanvasGroup CanvasGroup { get { return canvasGroupComponent; } }
  59. [SerializeField]
  60. private Text logText;
  61. [SerializeField]
  62. private Image logTypeImage;
  63. // Objects related to the collapsed count of the debug entry
  64. [SerializeField]
  65. private GameObject logCountParent;
  66. [SerializeField]
  67. private Text logCountText;
  68. [SerializeField]
  69. private RectTransform copyLogButton;
  70. #pragma warning restore 0649
  71. // Debug entry to show with this log item
  72. private DebugLogEntry logEntry;
  73. public DebugLogEntry Entry { get { return logEntry; } }
  74. private DebugLogEntryTimestamp? logEntryTimestamp;
  75. public DebugLogEntryTimestamp? Timestamp { get { return logEntryTimestamp; } }
  76. // Index of the entry in the list of entries
  77. private int entryIndex;
  78. public int Index { get { return entryIndex; } }
  79. private bool isExpanded;
  80. public bool Expanded { get { return isExpanded; } }
  81. private Vector2 logTextOriginalPosition;
  82. private Vector2 logTextOriginalSize;
  83. private float copyLogButtonHeight;
  84. private DebugLogRecycledListView listView;
  85. public void Initialize( DebugLogRecycledListView listView )
  86. {
  87. this.listView = listView;
  88. logTextOriginalPosition = logText.rectTransform.anchoredPosition;
  89. logTextOriginalSize = logText.rectTransform.sizeDelta;
  90. copyLogButtonHeight = copyLogButton.anchoredPosition.y + copyLogButton.sizeDelta.y + 2f; // 2f: space between text and button
  91. #if !UNITY_EDITOR && UNITY_WEBGL
  92. copyLogButton.gameObject.AddComponent<DebugLogItemCopyWebGL>().Initialize( this );
  93. #endif
  94. }
  95. public void SetContent( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, int entryIndex, bool isExpanded )
  96. {
  97. this.logEntry = logEntry;
  98. this.logEntryTimestamp = logEntryTimestamp;
  99. this.entryIndex = entryIndex;
  100. this.isExpanded = isExpanded;
  101. Vector2 size = transformComponent.sizeDelta;
  102. if( isExpanded )
  103. {
  104. logText.horizontalOverflow = HorizontalWrapMode.Wrap;
  105. size.y = listView.SelectedItemHeight;
  106. if( !copyLogButton.gameObject.activeSelf )
  107. {
  108. copyLogButton.gameObject.SetActive( true );
  109. logText.rectTransform.anchoredPosition = new Vector2( logTextOriginalPosition.x, logTextOriginalPosition.y + copyLogButtonHeight * 0.5f );
  110. logText.rectTransform.sizeDelta = logTextOriginalSize - new Vector2( 0f, copyLogButtonHeight );
  111. }
  112. }
  113. else
  114. {
  115. logText.horizontalOverflow = HorizontalWrapMode.Overflow;
  116. size.y = listView.ItemHeight;
  117. if( copyLogButton.gameObject.activeSelf )
  118. {
  119. copyLogButton.gameObject.SetActive( false );
  120. logText.rectTransform.anchoredPosition = logTextOriginalPosition;
  121. logText.rectTransform.sizeDelta = logTextOriginalSize;
  122. }
  123. }
  124. transformComponent.sizeDelta = size;
  125. SetText( logEntry, logEntryTimestamp, isExpanded );
  126. logTypeImage.sprite = logEntry.logTypeSpriteRepresentation;
  127. }
  128. // Show the collapsed count of the debug entry
  129. public void ShowCount()
  130. {
  131. logCountText.text = logEntry.count.ToString();
  132. if( !logCountParent.activeSelf )
  133. logCountParent.SetActive( true );
  134. }
  135. // Hide the collapsed count of the debug entry
  136. public void HideCount()
  137. {
  138. if( logCountParent.activeSelf )
  139. logCountParent.SetActive( false );
  140. }
  141. // Update the debug entry's displayed timestamp
  142. public void UpdateTimestamp( DebugLogEntryTimestamp timestamp )
  143. {
  144. logEntryTimestamp = timestamp;
  145. if( isExpanded || listView.manager.alwaysDisplayTimestamps )
  146. SetText( logEntry, timestamp, isExpanded );
  147. }
  148. private void SetText( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, bool isExpanded )
  149. {
  150. if( !logEntryTimestamp.HasValue || ( !isExpanded && !listView.manager.alwaysDisplayTimestamps ) )
  151. logText.text = isExpanded ? logEntry.ToString() : logEntry.logString;
  152. else
  153. {
  154. StringBuilder sb = listView.manager.sharedStringBuilder;
  155. sb.Length = 0;
  156. if( isExpanded )
  157. {
  158. logEntryTimestamp.Value.AppendFullTimestamp( sb );
  159. sb.Append( ": " ).Append( logEntry.ToString() );
  160. }
  161. else
  162. {
  163. logEntryTimestamp.Value.AppendTime( sb );
  164. sb.Append( " " ).Append( logEntry.logString );
  165. }
  166. logText.text = sb.ToString();
  167. }
  168. }
  169. // This log item is clicked, show the debug entry's stack trace
  170. public void OnPointerClick( PointerEventData eventData )
  171. {
  172. #if UNITY_EDITOR
  173. if( eventData.button == PointerEventData.InputButton.Right )
  174. {
  175. Match regex = Regex.Match( logEntry.stackTrace, @"\(at .*\.cs:[0-9]+\)$", RegexOptions.Multiline );
  176. if( regex.Success )
  177. {
  178. string line = logEntry.stackTrace.Substring( regex.Index + 4, regex.Length - 5 );
  179. int lineSeparator = line.IndexOf( ':' );
  180. MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>( line.Substring( 0, lineSeparator ) );
  181. if( script != null )
  182. AssetDatabase.OpenAsset( script, int.Parse( line.Substring( lineSeparator + 1 ) ) );
  183. }
  184. }
  185. else
  186. listView.OnLogItemClicked( this );
  187. #else
  188. listView.OnLogItemClicked( this );
  189. #endif
  190. }
  191. public void CopyLog()
  192. {
  193. #if UNITY_EDITOR || !UNITY_WEBGL
  194. string log = GetCopyContent();
  195. if( string.IsNullOrEmpty( log ) )
  196. return;
  197. #if UNITY_EDITOR || UNITY_2018_1_OR_NEWER || ( !UNITY_ANDROID && !UNITY_IOS )
  198. GUIUtility.systemCopyBuffer = log;
  199. #elif UNITY_ANDROID
  200. AJC.CallStatic( "CopyText", Context, log );
  201. #elif UNITY_IOS
  202. _DebugConsole_CopyText( log );
  203. #endif
  204. #endif
  205. }
  206. internal string GetCopyContent()
  207. {
  208. if( !logEntryTimestamp.HasValue )
  209. return logEntry.ToString();
  210. else
  211. {
  212. StringBuilder sb = listView.manager.sharedStringBuilder;
  213. sb.Length = 0;
  214. logEntryTimestamp.Value.AppendFullTimestamp( sb );
  215. sb.Append( ": " ).Append( logEntry.ToString() );
  216. return sb.ToString();
  217. }
  218. }
  219. public float CalculateExpandedHeight( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp )
  220. {
  221. string text = logText.text;
  222. HorizontalWrapMode wrapMode = logText.horizontalOverflow;
  223. SetText( logEntry, logEntryTimestamp, true );
  224. logText.horizontalOverflow = HorizontalWrapMode.Wrap;
  225. float result = logText.preferredHeight + copyLogButtonHeight;
  226. logText.text = text;
  227. logText.horizontalOverflow = wrapMode;
  228. return Mathf.Max( listView.ItemHeight, result );
  229. }
  230. // Return a string containing complete information about the debug entry
  231. public override string ToString()
  232. {
  233. return logEntry.ToString();
  234. }
  235. }
  236. }