1- using System . Collections . Generic ;
1+ using System ;
2+ using System . Collections . Generic ;
23using System . Text ;
34
45using UnityEditor ;
@@ -15,6 +16,15 @@ public abstract class HierarchyPropertyLabel
1516 {
1617 protected GameObject target ;
1718
19+ /// <summary>
20+ /// Does this label draw over the whole item?
21+ /// </summary>
22+ public virtual bool UsesWholeItemRect { get ; } = false ;
23+
24+ /// <summary>
25+ /// Should this label draw for headers too?
26+ /// </summary>
27+ public virtual bool DrawForHeaders { get ; } = false ;
1828
1929 public virtual bool Prepare ( GameObject target , Rect availableRect )
2030 {
@@ -60,6 +70,8 @@ public static HierarchyPropertyLabel GetPropertyLabel(HierarchyItemDataType data
6070 return new HierarchyLayerLabel ( ) ;
6171 case HierarchyItemDataType . Script :
6272 return new HierarchyScriptLabel ( ) ;
73+ case HierarchyItemDataType . TreeLines :
74+ return new HierarchyTreeLinesLabel ( ) ;
6375 }
6476
6577 return null ;
@@ -282,6 +294,159 @@ public override void OnGui(Rect rect)
282294 }
283295 }
284296
297+ private class HierarchyTreeLinesLabel : HierarchyPropertyLabel , IDisposable
298+ {
299+ private const float firstElementWidthOffset = 4.0f ;
300+ private const float firstElementXOffset = - 45.0f ;
301+ private const float startXPosition = 30.0f ;
302+ private const float columnSize = 14.0f ;
303+
304+ private readonly List < TreeLineLevelRenderer > levelRenderers = new List < TreeLineLevelRenderer > ( ) ;
305+ private int itemRenderCount = 0 ;
306+
307+ public HierarchyTreeLinesLabel ( )
308+ {
309+ EditorApplication . update += ResetItemRenderCount ;
310+ }
311+
312+ public void Dispose ( )
313+ {
314+ EditorApplication . update -= ResetItemRenderCount ;
315+ }
316+
317+ public override sealed void OnGui ( Rect rect )
318+ {
319+ if ( Event . current . type != EventType . Repaint )
320+ {
321+ return ;
322+ }
323+
324+ var levels = ( int ) ( ( rect . x + firstElementXOffset ) / columnSize ) ;
325+
326+ if ( levels <= 0 )
327+ {
328+ return ;
329+ }
330+
331+ if ( IsFirstRenderedElement )
332+ {
333+ levelRenderers . Clear ( ) ;
334+ }
335+
336+ itemRenderCount ++ ;
337+
338+ rect . x = startXPosition ;
339+ rect . width = columnSize + firstElementWidthOffset ;
340+
341+ var targetTransform = target . transform ;
342+ var siblingIndex = targetTransform . GetSiblingIndex ( ) ;
343+
344+ if ( levels > levelRenderers . Count )
345+ {
346+ //Initialize missing tree line level render
347+ var startIndex = levelRenderers . Count ;
348+ int x ;
349+ for ( x = startIndex ; x < levels ; x ++ )
350+ {
351+ var levelRenderer = new TreeLineLevelRenderer ( ) ;
352+ levelRenderers . Add ( levelRenderer ) ;
353+ }
354+
355+ x -- ;
356+
357+ Transform transformBuffer = targetTransform ;
358+ for ( ; x >= startIndex ; x -- )
359+ {
360+ levelRenderers [ x ] . Initialize ( transformBuffer ) ;
361+ transformBuffer = transformBuffer . parent ;
362+ }
363+ }
364+
365+ Color colorCache = GUI . color ;
366+ GUI . color = Color . gray ;
367+
368+ int i = 0 ;
369+ for ( ; i < ( levels - 1 ) ; i ++ )
370+ {
371+ levelRenderers [ i ] . OnGUI ( rect , target , siblingIndex , false ) ;
372+ rect . x += columnSize ;
373+ }
374+
375+ levelRenderers [ i ] . OnGUI ( rect , target , siblingIndex , true ) ;
376+
377+ GUI . color = colorCache ;
378+ }
379+
380+ private void ResetItemRenderCount ( )
381+ {
382+ itemRenderCount = 0 ;
383+ }
384+
385+ public override sealed bool UsesWholeItemRect => true ;
386+
387+ public override sealed bool DrawForHeaders => true ;
388+
389+ private bool IsFirstRenderedElement => itemRenderCount == 0 ;
390+
391+ private class TreeLineLevelRenderer
392+ {
393+ private bool renderedLastLevelGameobject = false ;
394+
395+ public void Initialize ( Transform transform )
396+ {
397+ var siblingIndex = transform . GetSiblingIndex ( ) ;
398+ renderedLastLevelGameobject = GetParentChildCount ( transform ) == ( siblingIndex + 1 ) ;
399+ }
400+
401+ public void OnGUI ( Rect rect , GameObject target , int siblingIndex , bool isCurrentLevel )
402+ {
403+ if ( isCurrentLevel )
404+ {
405+ if ( GetParentChildCount ( target ) == ( siblingIndex + 1 ) )
406+ {
407+ renderedLastLevelGameobject = true ;
408+ EditorGUI . LabelField ( rect , Style . elementLast , Style . centreAlignTreeLineStyle ) ;
409+ }
410+ else
411+ {
412+ renderedLastLevelGameobject = false ;
413+ EditorGUI . LabelField ( rect , Style . elementCross , Style . centreAlignTreeLineStyle ) ;
414+ }
415+ }
416+ else
417+ {
418+ if ( ! renderedLastLevelGameobject )
419+ {
420+ EditorGUI . LabelField ( rect , Style . elementPass , Style . centreAlignTreeLineStyle ) ;
421+ }
422+ }
423+ }
424+
425+ private int GetParentChildCount ( Transform transform )
426+ {
427+ var parent = transform . parent ;
428+ if ( parent != null )
429+ {
430+ return parent . childCount ;
431+ }
432+
433+ var scene = transform . gameObject . scene ;
434+ return scene . rootCount ;
435+ }
436+
437+ private int GetParentChildCount ( GameObject gameObject )
438+ {
439+ var parent = gameObject . transform . parent ;
440+ if ( parent != null )
441+ {
442+ return parent . childCount ;
443+ }
444+
445+ var scene = gameObject . scene ;
446+ return scene . rootCount ;
447+ }
448+ }
449+ }
285450 #endregion
286451
287452 protected static class Style
@@ -292,9 +457,18 @@ protected static class Style
292457 internal static readonly GUIStyle defaultAlignTextStyle ;
293458 internal static readonly GUIStyle centreAlignTextStyle ;
294459 internal static readonly GUIStyle rightAlignTextStyle ;
460+ internal static readonly GUIStyle centreAlignTreeLineStyle ;
461+
462+ internal static readonly GUIContent elementLast ;
463+ internal static readonly GUIContent elementCross ;
464+ internal static readonly GUIContent elementPass ;
295465
296466 static Style ( )
297467 {
468+ elementLast = new GUIContent ( "└" ) ;
469+ elementCross = new GUIContent ( "├" ) ;
470+ elementPass = new GUIContent ( "│" ) ;
471+
298472 defaultAlignTextStyle = new GUIStyle ( EditorStyles . miniLabel )
299473 {
300474#if UNITY_2019_3_OR_NEWER
@@ -307,28 +481,26 @@ static Style()
307481 {
308482#if UNITY_2019_3_OR_NEWER
309483 fontSize = 9 ,
310- #else
311- fontSize = 8 ,
312- #endif
313- #if UNITY_2019_3_OR_NEWER
314484 alignment = TextAnchor . MiddleCenter
315485#else
486+ fontSize = 8 ,
316487 alignment = TextAnchor . UpperCenter
317488#endif
318489 } ;
319490 rightAlignTextStyle = new GUIStyle ( EditorStyles . miniLabel )
320491 {
321492#if UNITY_2019_3_OR_NEWER
322493 fontSize = 9 ,
323- #else
324- fontSize = 8 ,
325- #endif
326- #if UNITY_2019_3_OR_NEWER
327494 alignment = TextAnchor . MiddleRight
328495#else
496+ fontSize = 8 ,
329497 alignment = TextAnchor . UpperRight
330498#endif
331499 } ;
500+ centreAlignTreeLineStyle = new GUIStyle ( EditorStyles . miniLabel )
501+ {
502+ fontSize = 18 ,
503+ } ;
332504 }
333505 }
334506 }
0 commit comments