1
- using System . Collections . Generic ;
1
+ using System ;
2
+ using System . Collections . Generic ;
2
3
using System . Text ;
3
4
4
5
using UnityEditor ;
@@ -15,6 +16,15 @@ public abstract class HierarchyPropertyLabel
15
16
{
16
17
protected GameObject target ;
17
18
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 ;
18
28
19
29
public virtual bool Prepare ( GameObject target , Rect availableRect )
20
30
{
@@ -60,6 +70,8 @@ public static HierarchyPropertyLabel GetPropertyLabel(HierarchyItemDataType data
60
70
return new HierarchyLayerLabel ( ) ;
61
71
case HierarchyItemDataType . Script :
62
72
return new HierarchyScriptLabel ( ) ;
73
+ case HierarchyItemDataType . TreeLines :
74
+ return new HierarchyTreeLinesLabel ( ) ;
63
75
}
64
76
65
77
return null ;
@@ -282,6 +294,159 @@ public override void OnGui(Rect rect)
282
294
}
283
295
}
284
296
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
+ }
285
450
#endregion
286
451
287
452
protected static class Style
@@ -292,9 +457,18 @@ protected static class Style
292
457
internal static readonly GUIStyle defaultAlignTextStyle ;
293
458
internal static readonly GUIStyle centreAlignTextStyle ;
294
459
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 ;
295
465
296
466
static Style ( )
297
467
{
468
+ elementLast = new GUIContent ( "└" ) ;
469
+ elementCross = new GUIContent ( "├" ) ;
470
+ elementPass = new GUIContent ( "│" ) ;
471
+
298
472
defaultAlignTextStyle = new GUIStyle ( EditorStyles . miniLabel )
299
473
{
300
474
#if UNITY_2019_3_OR_NEWER
@@ -307,28 +481,26 @@ static Style()
307
481
{
308
482
#if UNITY_2019_3_OR_NEWER
309
483
fontSize = 9 ,
310
- #else
311
- fontSize = 8 ,
312
- #endif
313
- #if UNITY_2019_3_OR_NEWER
314
484
alignment = TextAnchor . MiddleCenter
315
485
#else
486
+ fontSize = 8 ,
316
487
alignment = TextAnchor . UpperCenter
317
488
#endif
318
489
} ;
319
490
rightAlignTextStyle = new GUIStyle ( EditorStyles . miniLabel )
320
491
{
321
492
#if UNITY_2019_3_OR_NEWER
322
493
fontSize = 9 ,
323
- #else
324
- fontSize = 8 ,
325
- #endif
326
- #if UNITY_2019_3_OR_NEWER
327
494
alignment = TextAnchor . MiddleRight
328
495
#else
496
+ fontSize = 8 ,
329
497
alignment = TextAnchor . UpperRight
330
498
#endif
331
499
} ;
500
+ centreAlignTreeLineStyle = new GUIStyle ( EditorStyles . miniLabel )
501
+ {
502
+ fontSize = 18 ,
503
+ } ;
332
504
}
333
505
}
334
506
}
0 commit comments