Skip to content

Commit 04fe68f

Browse files
committed
Implemented hierarchy tree lines renderer
1 parent acfa0ba commit 04fe68f

4 files changed

Lines changed: 207 additions & 10 deletions

File tree

Assets/Editor Toolbox/Editor/Hierarchy/HierarchyItemDataType.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public enum HierarchyItemDataType
66
Toggle,
77
Tag,
88
Layer,
9-
Script
9+
Script,
10+
TreeLines
1011
}
1112
}

Assets/Editor Toolbox/Editor/Hierarchy/HierarchyPropertyLabel.cs

Lines changed: 176 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public abstract class HierarchyPropertyLabel
1515
{
1616
protected GameObject target;
1717

18+
/// <summary>
19+
/// Does this label draw over the whole item?
20+
/// </summary>
21+
public virtual bool UsesWholeItemRect { get; } = false;
22+
23+
/// <summary>
24+
/// Should this label draw for headers too?
25+
/// </summary>
26+
public virtual bool DrawForHeaders { get; } = false;
1827

1928
public virtual bool Prepare(GameObject target, Rect availableRect)
2029
{
@@ -60,6 +69,8 @@ public static HierarchyPropertyLabel GetPropertyLabel(HierarchyItemDataType data
6069
return new HierarchyLayerLabel();
6170
case HierarchyItemDataType.Script:
6271
return new HierarchyScriptLabel();
72+
case HierarchyItemDataType.TreeLines:
73+
return new HierarchyTreeLinesLabel();
6374
}
6475

6576
return null;
@@ -282,6 +293,164 @@ public override void OnGui(Rect rect)
282293
}
283294
}
284295

296+
private class HierarchyTreeLinesLabel : HierarchyPropertyLabel
297+
{
298+
private List<TreeLineLevelRenderer> levelRenderers = new List<TreeLineLevelRenderer>();
299+
300+
private int itemRenderCount = 0;
301+
302+
private const float firstElementWidthOffset = 4.0f;
303+
private const float firstElementXOffset = -45.0f;
304+
private const float startXPosition = 30.0f;
305+
private const float columnSize = 14.0f;
306+
307+
public override sealed bool UsesWholeItemRect => true;
308+
309+
public override sealed bool DrawForHeaders => true;
310+
311+
private bool IsFirstRenderedElement => itemRenderCount == 0;
312+
313+
public HierarchyTreeLinesLabel()
314+
{
315+
EditorApplication.update += ResetItemRenderCount;
316+
}
317+
318+
~HierarchyTreeLinesLabel()
319+
{
320+
EditorApplication.update -= ResetItemRenderCount;
321+
}
322+
323+
private void ResetItemRenderCount()
324+
{
325+
itemRenderCount = 0;
326+
}
327+
328+
public override sealed void OnGui(Rect rect)
329+
{
330+
if (Event.current.type != EventType.Repaint)
331+
{
332+
return;
333+
}
334+
335+
int levels = (int)((rect.x + firstElementXOffset) / columnSize);
336+
337+
if(levels <= 0)
338+
{
339+
return;
340+
}
341+
342+
if (IsFirstRenderedElement)
343+
{
344+
levelRenderers.Clear();
345+
}
346+
347+
rect.x = startXPosition;
348+
rect.width = columnSize + firstElementWidthOffset;
349+
350+
int siblingIndex = target.transform.GetSiblingIndex();
351+
352+
if (levels > levelRenderers.Count)
353+
{
354+
//Initialize missing tree line level render
355+
int startIndex = levelRenderers.Count;
356+
int x;
357+
for (x = startIndex; x < levels; x++)
358+
{
359+
var levelRenderer = new TreeLineLevelRenderer(x + 1);
360+
levelRenderers.Add(levelRenderer);
361+
}
362+
363+
x--;
364+
Transform transf = target.transform;
365+
366+
for (; x >= startIndex; x--)
367+
{
368+
levelRenderers[x].Initialize(transf);
369+
transf = transf.parent;
370+
}
371+
}
372+
373+
GUI.color = Color.gray;
374+
375+
itemRenderCount++;
376+
int i = 0;
377+
for (; i < (levels - 1); i++)
378+
{
379+
levelRenderers[i].OnGUI(rect, target, siblingIndex, false);
380+
rect.x += columnSize;
381+
}
382+
levelRenderers[i].OnGUI(rect, target, siblingIndex, true);
383+
384+
GUI.color = Color.white;
385+
}
386+
387+
private class TreeLineLevelRenderer
388+
{
389+
private bool renderedLastLevelGameobject = false;
390+
private int level;
391+
392+
public TreeLineLevelRenderer(int level)
393+
{
394+
this.level = level;
395+
}
396+
397+
public void Initialize(Transform transf)
398+
{
399+
int siblingIndex = transf.GetSiblingIndex();
400+
renderedLastLevelGameobject = GetParentChildCount(transf) == (siblingIndex + 1);
401+
}
402+
403+
public void OnGUI(Rect rect, GameObject target, int siblingIndex, bool isCurrentLevel)
404+
{
405+
if (isCurrentLevel)
406+
{
407+
if (GetParentChildCount(target) == (siblingIndex + 1))
408+
{
409+
renderedLastLevelGameobject = true;
410+
EditorGUI.LabelField(rect, "└", Style.centreAlignTreeLineStyle);
411+
}
412+
else
413+
{
414+
renderedLastLevelGameobject = false;
415+
EditorGUI.LabelField(rect, "├", Style.centreAlignTreeLineStyle);
416+
}
417+
}
418+
else
419+
{
420+
if (!renderedLastLevelGameobject)
421+
{
422+
EditorGUI.LabelField(rect, "│", Style.centreAlignTreeLineStyle);
423+
}
424+
}
425+
}
426+
427+
private int GetParentChildCount(Transform target)
428+
{
429+
if (level == 1)
430+
{
431+
var scene = target.gameObject.scene;
432+
return scene.rootCount;
433+
}
434+
else
435+
{
436+
var parent = target.parent;
437+
return parent.childCount;
438+
}
439+
}
440+
441+
private int GetParentChildCount(GameObject target)
442+
{
443+
var parent = target.transform.parent;
444+
if(parent != null)
445+
{
446+
return parent.childCount;
447+
}
448+
449+
var scene = target.scene;
450+
return scene.rootCount;
451+
}
452+
}
453+
}
285454
#endregion
286455

287456
protected static class Style
@@ -292,6 +461,7 @@ protected static class Style
292461
internal static readonly GUIStyle defaultAlignTextStyle;
293462
internal static readonly GUIStyle centreAlignTextStyle;
294463
internal static readonly GUIStyle rightAlignTextStyle;
464+
internal static readonly GUIStyle centreAlignTreeLineStyle;
295465

296466
static Style()
297467
{
@@ -307,28 +477,26 @@ static Style()
307477
{
308478
#if UNITY_2019_3_OR_NEWER
309479
fontSize = 9,
310-
#else
311-
fontSize = 8,
312-
#endif
313-
#if UNITY_2019_3_OR_NEWER
314480
alignment = TextAnchor.MiddleCenter
315481
#else
482+
fontSize = 8,
316483
alignment = TextAnchor.UpperCenter
317484
#endif
318485
};
319486
rightAlignTextStyle = new GUIStyle(EditorStyles.miniLabel)
320487
{
321488
#if UNITY_2019_3_OR_NEWER
322489
fontSize = 9,
323-
#else
324-
fontSize = 8,
325-
#endif
326-
#if UNITY_2019_3_OR_NEWER
327490
alignment = TextAnchor.MiddleRight
328491
#else
492+
fontSize = 8,
329493
alignment = TextAnchor.UpperRight
330494
#endif
331495
};
496+
centreAlignTreeLineStyle = new GUIStyle(EditorStyles.miniLabel)
497+
{
498+
fontSize = 18,
499+
};
332500
}
333501
}
334502
}

Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,23 @@ private static void DrawHeaderItemLabel(Rect rect, GameObject gameObject, string
124124
var itemContent = new GUIContent(label, iconContent.image);
125125

126126
EditorGUI.LabelField(rect, itemContent, Style.headerLabelStyle);
127+
128+
var contentRect = rect;
129+
var labelsCount = propertyLabels.Count;
130+
var availableRect = contentRect;
131+
132+
for (var i = 0; i < labelsCount; i++)
133+
{
134+
if (!propertyLabels[i].DrawForHeaders)
135+
{
136+
continue;
137+
}
138+
139+
contentRect = AppendPropertyLabel(propertyLabels[i], gameObject, availableRect);
140+
availableRect.xMax -= contentRect.width;
141+
142+
EditorGUI.DrawRect(new Rect(contentRect.xMin, rect.y, Style.lineWidth, rect.height), Style.lineColor);
143+
}
127144
}
128145

129146
/// <summary>
@@ -167,6 +184,16 @@ private static void DrawDefaultItemLabel(Rect rect, GameObject gameObject, strin
167184

168185
private static Rect AppendPropertyLabel(HierarchyPropertyLabel propertyLabel, GameObject target, Rect availableRect)
169186
{
187+
if (propertyLabel.UsesWholeItemRect)
188+
{
189+
if(propertyLabel.Prepare(target, availableRect))
190+
{
191+
propertyLabel.OnGui(availableRect);
192+
}
193+
194+
return availableRect;
195+
}
196+
170197
//prepare currently used property label
171198
if (propertyLabel.Prepare(target, availableRect, out var width))
172199
{

Assets/Editor Toolbox/Editor/ToolboxEditorSettings.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ internal void Validate()
192192
private void OnValidate()
193193
{
194194
//determine if any section was changed within the Editor
195-
var settingsDirty = hierarchySettingsDirty || projectSettingsDirty || inspectorSettingsDirty;
195+
var settingsDirty = hierarchySettingsDirty || projectSettingsDirty || inspectorSettingsDirty || sceneViewSettingsDirty;
196196
if (settingsDirty)
197197
{
198198
//check exactly what settings are changed and apply them
@@ -229,6 +229,7 @@ private void OnValidate()
229229
hierarchySettingsDirty = false;
230230
projectSettingsDirty = false;
231231
inspectorSettingsDirty = false;
232+
sceneViewSettingsDirty = false;
232233
}
233234

234235
#endregion

0 commit comments

Comments
 (0)