77
88namespace Toolbox . Editor . Hierarchy
99{
10- //TODO: refactor: replace labels with drawers (similar approach to the Inspector), possibility to define drawers and implement them using a dedicated base class
10+ //TODO: refactor: replace labels with drawers (similar approach to the Inspector), possibility to define drawers and implement them using a dedicated base class & SerializeReference approach
1111
1212 /// <summary>
1313 /// Base class for all custom, Hierarchy-related labels based on targeted <see cref="GameObject"/>.
@@ -134,31 +134,54 @@ public override void OnGui(Rect rect)
134134
135135 private class HierarchyLayerLabel : HierarchyPropertyLabel
136136 {
137- public override void OnGui ( Rect rect )
137+ private GUIContent content ;
138+
139+ //NOTE: after replacing with SerializeReference-based implementation we should allow to pick if layer should be simplied (just number) or fully displayed
140+ private string GetContentText ( LayerMask layerMask )
138141 {
139- var layerMask = target . layer ;
140142 var layerName = LayerMask . LayerToName ( layerMask ) ;
143+ switch ( layerMask )
144+ {
145+ case 00 : return string . Empty ;
146+ default : return layerName ;
147+ }
148+ }
149+
150+ public override bool Prepare ( GameObject target , Rect availableRect )
151+ {
152+ if ( ! base . Prepare ( target , availableRect ) )
153+ {
154+ return false ;
155+ }
156+
157+ var layerMask = target . layer ;
158+ var layerName = GetContentText ( layerMask ) ;
159+ content = new GUIContent ( layerName ) ;
160+ return true ;
161+ }
141162
142- var contentText = GetContentText ( ) ;
143- var content = new GUIContent ( contentText , $ " { layerName } layer" ) ;
163+ public override void OnGui ( Rect rect )
164+ {
144165 EditorGUI . LabelField ( rect , content , Style . centreAlignTextStyle ) ;
166+ }
145167
146- string GetContentText ( )
168+ public override float GetWidth ( )
169+ {
170+ if ( string . IsNullOrEmpty ( content . text ) )
147171 {
148- switch ( layerMask )
149- {
150- case 00 : return string . Empty ;
151- case 05 : return layerName ;
152- default : return layerMask . ToString ( ) ;
153- }
172+ return base . GetWidth ( ) ;
154173 }
174+
175+ var size = Style . centreAlignTextStyle . CalcSize ( content ) ;
176+ return size . x + EditorGUIUtility . standardVerticalSpacing * 2 ;
155177 }
156178 }
157179
158180 private class HierarchyScriptLabel : HierarchyPropertyLabel
159181 {
160182 private static Texture componentIcon ;
161183 private static Texture transformIcon ;
184+ private static Texture warningIcon ;
162185
163186 /// <summary>
164187 /// Cached components of the last prepared <see cref="target"/>.
@@ -178,13 +201,8 @@ private GUIContent GetTooltipContent()
178201 for ( var i = 1 ; i < componentsCount ; i ++ )
179202 {
180203 var component = components [ i ] ;
181- if ( component == null )
182- {
183- continue ;
184- }
185-
186204 tooltipBuilder . Append ( "- " ) ;
187- tooltipBuilder . Append ( component . GetType ( ) . Name ) ;
205+ tooltipBuilder . Append ( component != null ? component . GetType ( ) . Name : "<null>" ) ;
188206 if ( componentsCount - 1 != i )
189207 {
190208 tooltipBuilder . Append ( "\n " ) ;
@@ -197,6 +215,11 @@ private GUIContent GetTooltipContent()
197215
198216 private GUIContent GetContent ( Component component )
199217 {
218+ if ( component == null )
219+ {
220+ return new GUIContent ( image : warningIcon ) ;
221+ }
222+
200223 var content = EditorGUIUtility . ObjectContent ( component , component . GetType ( ) ) ;
201224 content . text = string . Empty ;
202225 if ( content . image == null )
@@ -207,6 +230,13 @@ private GUIContent GetContent(Component component)
207230 return content ;
208231 }
209232
233+ private void CachePredefinedIcons ( )
234+ {
235+ componentIcon = componentIcon != null ? componentIcon : EditorGUIUtility . IconContent ( "cs Script Icon" ) . image ;
236+ transformIcon = transformIcon != null ? transformIcon : EditorGUIUtility . IconContent ( "Transform Icon" ) . image ;
237+ warningIcon = warningIcon != null ? warningIcon : EditorGUIUtility . IconContent ( "console.warnicon.sml" ) . image ;
238+ }
239+
210240 public override bool Prepare ( GameObject target , Rect availableRect )
211241 {
212242 var isValid = base . Prepare ( target , availableRect ) ;
@@ -224,8 +254,7 @@ public override bool Prepare(GameObject target, Rect availableRect)
224254
225255 isHighlighted = availableRect . Contains ( Event . current . mousePosition ) ;
226256
227- componentIcon = componentIcon != null ? componentIcon : EditorGUIUtility . IconContent ( "cs Script Icon" ) . image ;
228- transformIcon = transformIcon != null ? transformIcon : EditorGUIUtility . IconContent ( "Transform Icon" ) . image ;
257+ CachePredefinedIcons ( ) ;
229258 return true ;
230259 }
231260
@@ -256,11 +285,6 @@ public override void OnGui(Rect rect)
256285 for ( var i = 1 ; i < components . Length ; i ++ )
257286 {
258287 var component = components [ i ] ;
259- if ( component == null )
260- {
261- continue ;
262- }
263-
264288 var content = GetContent ( component ) ;
265289 //draw icon for the current component
266290 GUI . Label ( iconRect , content ) ;
@@ -279,8 +303,14 @@ public override void OnGui(Rect rect)
279303 private class HierarchyTreeLinesLabel : HierarchyPropertyLabel , IDisposable
280304 {
281305 private const float firstElementWidthOffset = 4.0f ;
306+
307+ #if UNITY_2019_1_OR_NEWER
282308 private const float firstElementXOffset = - 45.0f ;
283309 private const float startXPosition = 30.0f ;
310+ #else
311+ private const float firstElementXOffset = - 15.0f ;
312+ private const float startXPosition = 0.0f ;
313+ #endif
284314 private const float columnSize = 14.0f ;
285315
286316 private readonly List < TreeLineLevelRenderer > levelRenderers = new List < TreeLineLevelRenderer > ( ) ;
@@ -318,14 +348,15 @@ public sealed override void OnGui(Rect rect)
318348 itemRenderCount ++ ;
319349
320350 rect . x = startXPosition ;
321- rect . width = columnSize + firstElementWidthOffset ;
351+ //we need 2x column size for full-line cases when object has no children and there is no foldout
352+ rect . width = 2 * columnSize + firstElementWidthOffset ;
322353
323354 var targetTransform = target . transform ;
324355 var siblingIndex = targetTransform . GetSiblingIndex ( ) ;
325356
326357 if ( levels > levelRenderers . Count )
327358 {
328- //Initialize missing tree line level render
359+ //initialize missing tree line level render
329360 var startIndex = levelRenderers . Count ;
330361 int x ;
331362 for ( x = startIndex ; x < levels ; x ++ )
@@ -382,54 +413,56 @@ public void Initialize(Transform transform)
382413
383414 public void OnGUI ( Rect rect , GameObject target , int siblingIndex , bool isCurrentLevel )
384415 {
416+ //NOTE: currently we are using labels and predefined chars to display tree lines, this is not optimal solution
417+ // since we can't really control width, tickiness and other potential useful properties. Using few chars allow us
418+ // to display dashed lines very easily but replacing it with standard line would a bit harder.
419+ // For now this is ok solution but probably should be replaced with drawing lines using the EditorGUI.DrawRect API,
420+ // in the same way we draw lines in the Inspector
421+
385422 if ( isCurrentLevel )
386423 {
424+ var hasChildren = target . transform . childCount > 0 ;
425+ GUIContent label ;
387426 if ( GetParentChildCount ( target ) == ( siblingIndex + 1 ) )
388427 {
389428 renderedLastLevelGameobject = true ;
390- EditorGUI . LabelField ( rect , Style . treeElementLast , Style . treeElementStyle ) ;
429+ label = hasChildren ? Style . treeElementLastHalf : Style . treeElementLast ;
391430 }
392431 else
393432 {
394433 renderedLastLevelGameobject = false ;
395- EditorGUI . LabelField ( rect , Style . treeElementCross , Style . treeElementStyle ) ;
434+ label = hasChildren ? Style . treeElementCrossHalf : Style . treeElementCross ;
396435 }
436+
437+ EditorGUI . LabelField ( rect , label , Style . treeElementStyle ) ;
438+ return ;
397439 }
398- else
440+
441+ if ( ! renderedLastLevelGameobject )
399442 {
400- if ( ! renderedLastLevelGameobject )
401- {
402- EditorGUI . LabelField ( rect , Style . treeElementPass , Style . treeElementStyle ) ;
403- }
443+ EditorGUI . LabelField ( rect , Style . treeElementPass , Style . treeElementStyle ) ;
404444 }
405445 }
406446
407- private int GetParentChildCount ( Transform transform )
447+ private int GetParentChildCount ( GameObject gameObject )
408448 {
409- var parent = transform . parent ;
410- if ( parent != null )
411- {
412- return parent . childCount ;
413- }
414-
415- var scene = transform . gameObject . scene ;
416- return scene . rootCount ;
449+ return GetParentChildCount ( gameObject . transform ) ;
417450 }
418451
419- private int GetParentChildCount ( GameObject gameObject )
452+ private int GetParentChildCount ( Transform transform )
420453 {
421- var parent = gameObject . transform . parent ;
454+ var parent = transform . parent ;
422455 if ( parent != null )
423456 {
424457 return parent . childCount ;
425458 }
426459
427- var scene = gameObject . scene ;
460+ var scene = transform . gameObject . scene ;
428461 return scene . rootCount ;
429462 }
430463 }
431464 }
432- #endregion
465+ #endregion
433466
434467 protected static class Style
435468 {
@@ -442,15 +475,19 @@ protected static class Style
442475 internal static readonly GUIStyle treeElementStyle ;
443476
444477 internal static readonly GUIContent treeElementLast ;
478+ internal static readonly GUIContent treeElementLastHalf ;
445479 internal static readonly GUIContent treeElementCross ;
480+ internal static readonly GUIContent treeElementCrossHalf ;
446481 internal static readonly GUIContent treeElementPass ;
447482
448483 internal static readonly Color characterColor ;
449484
450485 static Style ( )
451486 {
452- treeElementLast = new GUIContent ( "└" ) ;
453- treeElementCross = new GUIContent ( "├" ) ;
487+ treeElementLast = new GUIContent ( "└--" ) ;
488+ treeElementLastHalf = new GUIContent ( "└-" ) ;
489+ treeElementCross = new GUIContent ( "├--" ) ;
490+ treeElementCrossHalf = new GUIContent ( "├-" ) ;
454491 treeElementPass = new GUIContent ( "│" ) ;
455492
456493 defaultAlignTextStyle = new GUIStyle ( EditorStyles . miniLabel )
@@ -481,9 +518,10 @@ static Style()
481518 alignment = TextAnchor . UpperRight
482519#endif
483520 } ;
484- treeElementStyle = new GUIStyle ( EditorStyles . miniLabel )
521+ treeElementStyle = new GUIStyle ( EditorStyles . label )
485522 {
486- fontSize = 16 ,
523+ padding = new RectOffset ( 4 , 0 , 0 , 0 ) ,
524+ fontSize = 12 ,
487525 } ;
488526
489527 if ( ! EditorGUIUtility . isProSkin )
0 commit comments