Skip to content

Commit d816c95

Browse files
committed
Fix rendering tree lines in Unity 2018; replace lines with dashed version (still using chars); extend lines for non-children objects; minor refactor changes
1 parent 8d44271 commit d816c95

File tree

5 files changed

+962
-120
lines changed

5 files changed

+962
-120
lines changed

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

Lines changed: 89 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace 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)

Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ private static void HandleHeaderObject(GameObject gameObject)
287287

288288
#endregion
289289

290-
internal static void CreateAllowedHierarchyContentCallbacks(params HierarchyItemDataType[] items)
290+
internal static void CreatePropertyLabels(params HierarchyItemDataType[] items)
291291
{
292292
foreach (var item in items)
293293
{
@@ -301,7 +301,7 @@ internal static void CreateAllowedHierarchyContentCallbacks(params HierarchyItem
301301
}
302302
}
303303

304-
internal static void RemoveAllowedHierarchyContentCallbacks()
304+
internal static void RemovePropertyLabels()
305305
{
306306
for (int i = 0; i < propertyLabels.Count; i++)
307307
{

Assets/Editor Toolbox/Editor/ToolboxManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ private static void ManageHierarchyCore(IToolboxHierarchySettings settings)
7373
ToolboxEditorHierarchy.ShowSelectionsCount = settings.ShowSelectionsCount;
7474
ToolboxEditorHierarchy.DrawSeparationLines = true;
7575

76-
ToolboxEditorHierarchy.RemoveAllowedHierarchyContentCallbacks();
76+
ToolboxEditorHierarchy.RemovePropertyLabels();
7777

7878
//create custom drawers using stored data
7979
for (var i = 0; i < settings.RowDataTypes.Count; i++)
8080
{
81-
ToolboxEditorHierarchy.CreateAllowedHierarchyContentCallbacks(settings.RowDataTypes[i]);
81+
ToolboxEditorHierarchy.CreatePropertyLabels(settings.RowDataTypes[i]);
8282
}
8383

8484
ToolboxEditorHierarchy.RepaintHierarchyOverlay();

Assets/Editor Toolbox/EditorSettings.asset

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ MonoBehaviour:
1515
useToolboxHierarchy: 1
1616
drawHorizontalLines: 1
1717
showSelectionsCount: 1
18-
rowDataTypes: 000000000100000005000000
18+
rowDataTypes: 00000000010000000500000004000000
1919
useToolboxFolders: 1
2020
largeIconScale: 0.8
2121
smallIconScale: 0.7
@@ -93,8 +93,7 @@ MonoBehaviour:
9393
- typeReference: Toolbox.Editor.Drawers.RegexValueAttributeDrawer, Toolbox.Editor
9494
listPropertyDrawerHandlers:
9595
- typeReference: Toolbox.Editor.Drawers.ReorderableListAttributeDrawer, Toolbox.Editor
96-
- typeReference: Toolbox.Editor.Drawers.ReorderableListExposedAttributeDrawer,
97-
Toolbox.Editor
96+
- typeReference: Toolbox.Editor.Drawers.ReorderableListExposedAttributeDrawer, Toolbox.Editor
9897
- typeReference: Toolbox.Editor.Drawers.ScrollableItemsAttributeDrawer, Toolbox.Editor
9998
targetTypeDrawerHandlers:
10099
- typeReference: Toolbox.Editor.Drawers.SerializedDictionaryDrawer, Toolbox.Editor

0 commit comments

Comments
 (0)