7
7
8
8
namespace Toolbox . Editor . Hierarchy
9
9
{
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
11
11
12
12
/// <summary>
13
13
/// Base class for all custom, Hierarchy-related labels based on targeted <see cref="GameObject"/>.
@@ -134,31 +134,54 @@ public override void OnGui(Rect rect)
134
134
135
135
private class HierarchyLayerLabel : HierarchyPropertyLabel
136
136
{
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 )
138
141
{
139
- var layerMask = target . layer ;
140
142
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
+ }
141
162
142
- var contentText = GetContentText ( ) ;
143
- var content = new GUIContent ( contentText , $ " { layerName } layer" ) ;
163
+ public override void OnGui ( Rect rect )
164
+ {
144
165
EditorGUI . LabelField ( rect , content , Style . centreAlignTextStyle ) ;
166
+ }
145
167
146
- string GetContentText ( )
168
+ public override float GetWidth ( )
169
+ {
170
+ if ( string . IsNullOrEmpty ( content . text ) )
147
171
{
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 ( ) ;
154
173
}
174
+
175
+ var size = Style . centreAlignTextStyle . CalcSize ( content ) ;
176
+ return size . x + EditorGUIUtility . standardVerticalSpacing * 2 ;
155
177
}
156
178
}
157
179
158
180
private class HierarchyScriptLabel : HierarchyPropertyLabel
159
181
{
160
182
private static Texture componentIcon ;
161
183
private static Texture transformIcon ;
184
+ private static Texture warningIcon ;
162
185
163
186
/// <summary>
164
187
/// Cached components of the last prepared <see cref="target"/>.
@@ -178,13 +201,8 @@ private GUIContent GetTooltipContent()
178
201
for ( var i = 1 ; i < componentsCount ; i ++ )
179
202
{
180
203
var component = components [ i ] ;
181
- if ( component == null )
182
- {
183
- continue ;
184
- }
185
-
186
204
tooltipBuilder . Append ( "- " ) ;
187
- tooltipBuilder . Append ( component . GetType ( ) . Name ) ;
205
+ tooltipBuilder . Append ( component != null ? component . GetType ( ) . Name : "<null>" ) ;
188
206
if ( componentsCount - 1 != i )
189
207
{
190
208
tooltipBuilder . Append ( "\n " ) ;
@@ -197,6 +215,11 @@ private GUIContent GetTooltipContent()
197
215
198
216
private GUIContent GetContent ( Component component )
199
217
{
218
+ if ( component == null )
219
+ {
220
+ return new GUIContent ( image : warningIcon ) ;
221
+ }
222
+
200
223
var content = EditorGUIUtility . ObjectContent ( component , component . GetType ( ) ) ;
201
224
content . text = string . Empty ;
202
225
if ( content . image == null )
@@ -207,6 +230,13 @@ private GUIContent GetContent(Component component)
207
230
return content ;
208
231
}
209
232
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
+
210
240
public override bool Prepare ( GameObject target , Rect availableRect )
211
241
{
212
242
var isValid = base . Prepare ( target , availableRect ) ;
@@ -224,8 +254,7 @@ public override bool Prepare(GameObject target, Rect availableRect)
224
254
225
255
isHighlighted = availableRect . Contains ( Event . current . mousePosition ) ;
226
256
227
- componentIcon = componentIcon != null ? componentIcon : EditorGUIUtility . IconContent ( "cs Script Icon" ) . image ;
228
- transformIcon = transformIcon != null ? transformIcon : EditorGUIUtility . IconContent ( "Transform Icon" ) . image ;
257
+ CachePredefinedIcons ( ) ;
229
258
return true ;
230
259
}
231
260
@@ -256,11 +285,6 @@ public override void OnGui(Rect rect)
256
285
for ( var i = 1 ; i < components . Length ; i ++ )
257
286
{
258
287
var component = components [ i ] ;
259
- if ( component == null )
260
- {
261
- continue ;
262
- }
263
-
264
288
var content = GetContent ( component ) ;
265
289
//draw icon for the current component
266
290
GUI . Label ( iconRect , content ) ;
@@ -279,8 +303,14 @@ public override void OnGui(Rect rect)
279
303
private class HierarchyTreeLinesLabel : HierarchyPropertyLabel , IDisposable
280
304
{
281
305
private const float firstElementWidthOffset = 4.0f ;
306
+
307
+ #if UNITY_2019_1_OR_NEWER
282
308
private const float firstElementXOffset = - 45.0f ;
283
309
private const float startXPosition = 30.0f ;
310
+ #else
311
+ private const float firstElementXOffset = - 15.0f ;
312
+ private const float startXPosition = 0.0f ;
313
+ #endif
284
314
private const float columnSize = 14.0f ;
285
315
286
316
private readonly List < TreeLineLevelRenderer > levelRenderers = new List < TreeLineLevelRenderer > ( ) ;
@@ -318,14 +348,15 @@ public sealed override void OnGui(Rect rect)
318
348
itemRenderCount ++ ;
319
349
320
350
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 ;
322
353
323
354
var targetTransform = target . transform ;
324
355
var siblingIndex = targetTransform . GetSiblingIndex ( ) ;
325
356
326
357
if ( levels > levelRenderers . Count )
327
358
{
328
- //Initialize missing tree line level render
359
+ //initialize missing tree line level render
329
360
var startIndex = levelRenderers . Count ;
330
361
int x ;
331
362
for ( x = startIndex ; x < levels ; x ++ )
@@ -382,54 +413,56 @@ public void Initialize(Transform transform)
382
413
383
414
public void OnGUI ( Rect rect , GameObject target , int siblingIndex , bool isCurrentLevel )
384
415
{
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
+
385
422
if ( isCurrentLevel )
386
423
{
424
+ var hasChildren = target . transform . childCount > 0 ;
425
+ GUIContent label ;
387
426
if ( GetParentChildCount ( target ) == ( siblingIndex + 1 ) )
388
427
{
389
428
renderedLastLevelGameobject = true ;
390
- EditorGUI . LabelField ( rect , Style . treeElementLast , Style . treeElementStyle ) ;
429
+ label = hasChildren ? Style . treeElementLastHalf : Style . treeElementLast ;
391
430
}
392
431
else
393
432
{
394
433
renderedLastLevelGameobject = false ;
395
- EditorGUI . LabelField ( rect , Style . treeElementCross , Style . treeElementStyle ) ;
434
+ label = hasChildren ? Style . treeElementCrossHalf : Style . treeElementCross ;
396
435
}
436
+
437
+ EditorGUI . LabelField ( rect , label , Style . treeElementStyle ) ;
438
+ return ;
397
439
}
398
- else
440
+
441
+ if ( ! renderedLastLevelGameobject )
399
442
{
400
- if ( ! renderedLastLevelGameobject )
401
- {
402
- EditorGUI . LabelField ( rect , Style . treeElementPass , Style . treeElementStyle ) ;
403
- }
443
+ EditorGUI . LabelField ( rect , Style . treeElementPass , Style . treeElementStyle ) ;
404
444
}
405
445
}
406
446
407
- private int GetParentChildCount ( Transform transform )
447
+ private int GetParentChildCount ( GameObject gameObject )
408
448
{
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 ) ;
417
450
}
418
451
419
- private int GetParentChildCount ( GameObject gameObject )
452
+ private int GetParentChildCount ( Transform transform )
420
453
{
421
- var parent = gameObject . transform . parent ;
454
+ var parent = transform . parent ;
422
455
if ( parent != null )
423
456
{
424
457
return parent . childCount ;
425
458
}
426
459
427
- var scene = gameObject . scene ;
460
+ var scene = transform . gameObject . scene ;
428
461
return scene . rootCount ;
429
462
}
430
463
}
431
464
}
432
- #endregion
465
+ #endregion
433
466
434
467
protected static class Style
435
468
{
@@ -442,15 +475,19 @@ protected static class Style
442
475
internal static readonly GUIStyle treeElementStyle ;
443
476
444
477
internal static readonly GUIContent treeElementLast ;
478
+ internal static readonly GUIContent treeElementLastHalf ;
445
479
internal static readonly GUIContent treeElementCross ;
480
+ internal static readonly GUIContent treeElementCrossHalf ;
446
481
internal static readonly GUIContent treeElementPass ;
447
482
448
483
internal static readonly Color characterColor ;
449
484
450
485
static Style ( )
451
486
{
452
- treeElementLast = new GUIContent ( "└" ) ;
453
- treeElementCross = new GUIContent ( "├" ) ;
487
+ treeElementLast = new GUIContent ( "└--" ) ;
488
+ treeElementLastHalf = new GUIContent ( "└-" ) ;
489
+ treeElementCross = new GUIContent ( "├--" ) ;
490
+ treeElementCrossHalf = new GUIContent ( "├-" ) ;
454
491
treeElementPass = new GUIContent ( "│" ) ;
455
492
456
493
defaultAlignTextStyle = new GUIStyle ( EditorStyles . miniLabel )
@@ -481,9 +518,10 @@ static Style()
481
518
alignment = TextAnchor . UpperRight
482
519
#endif
483
520
} ;
484
- treeElementStyle = new GUIStyle ( EditorStyles . miniLabel )
521
+ treeElementStyle = new GUIStyle ( EditorStyles . label )
485
522
{
486
- fontSize = 16 ,
523
+ padding = new RectOffset ( 4 , 0 , 0 , 0 ) ,
524
+ fontSize = 12 ,
487
525
} ;
488
526
489
527
if ( ! EditorGUIUtility . isProSkin )
0 commit comments