Skip to content

Commit

Permalink
Fix rendering tree lines in Unity 2018; replace lines with dashed ver…
Browse files Browse the repository at this point in the history
…sion (still using chars); extend lines for non-children objects; minor refactor changes
  • Loading branch information
arimger committed Aug 22, 2024
1 parent 8d44271 commit d816c95
Show file tree
Hide file tree
Showing 5 changed files with 962 additions and 120 deletions.
140 changes: 89 additions & 51 deletions Assets/Editor Toolbox/Editor/Hierarchy/HierarchyPropertyLabel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Toolbox.Editor.Hierarchy
{
//TODO: refactor: replace labels with drawers (similar approach to the Inspector), possibility to define drawers and implement them using a dedicated base class
//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

/// <summary>
/// Base class for all custom, Hierarchy-related labels based on targeted <see cref="GameObject"/>.
Expand Down Expand Up @@ -134,31 +134,54 @@ public override void OnGui(Rect rect)

private class HierarchyLayerLabel : HierarchyPropertyLabel
{
public override void OnGui(Rect rect)
private GUIContent content;

//NOTE: after replacing with SerializeReference-based implementation we should allow to pick if layer should be simplied (just number) or fully displayed
private string GetContentText(LayerMask layerMask)
{
var layerMask = target.layer;
var layerName = LayerMask.LayerToName(layerMask);
switch (layerMask)
{
case 00: return string.Empty;
default: return layerName;
}
}

public override bool Prepare(GameObject target, Rect availableRect)
{
if (!base.Prepare(target, availableRect))
{
return false;
}

var layerMask = target.layer;
var layerName = GetContentText(layerMask);
content = new GUIContent(layerName);
return true;
}

var contentText = GetContentText();
var content = new GUIContent(contentText, $"{layerName} layer");
public override void OnGui(Rect rect)
{
EditorGUI.LabelField(rect, content, Style.centreAlignTextStyle);
}

string GetContentText()
public override float GetWidth()
{
if (string.IsNullOrEmpty(content.text))
{
switch (layerMask)
{
case 00: return string.Empty;
case 05: return layerName;
default: return layerMask.ToString();
}
return base.GetWidth();
}

var size = Style.centreAlignTextStyle.CalcSize(content);
return size.x + EditorGUIUtility.standardVerticalSpacing * 2;
}
}

private class HierarchyScriptLabel : HierarchyPropertyLabel
{
private static Texture componentIcon;
private static Texture transformIcon;
private static Texture warningIcon;

/// <summary>
/// Cached components of the last prepared <see cref="target"/>.
Expand All @@ -178,13 +201,8 @@ private GUIContent GetTooltipContent()
for (var i = 1; i < componentsCount; i++)
{
var component = components[i];
if (component == null)
{
continue;
}

tooltipBuilder.Append("- ");
tooltipBuilder.Append(component.GetType().Name);
tooltipBuilder.Append(component != null ? component.GetType().Name : "<null>");
if (componentsCount - 1 != i)
{
tooltipBuilder.Append("\n");
Expand All @@ -197,6 +215,11 @@ private GUIContent GetTooltipContent()

private GUIContent GetContent(Component component)
{
if (component == null)
{
return new GUIContent(image: warningIcon);
}

var content = EditorGUIUtility.ObjectContent(component, component.GetType());
content.text = string.Empty;
if (content.image == null)
Expand All @@ -207,6 +230,13 @@ private GUIContent GetContent(Component component)
return content;
}

private void CachePredefinedIcons()
{
componentIcon = componentIcon != null ? componentIcon : EditorGUIUtility.IconContent("cs Script Icon").image;
transformIcon = transformIcon != null ? transformIcon : EditorGUIUtility.IconContent("Transform Icon").image;
warningIcon = warningIcon != null ? warningIcon : EditorGUIUtility.IconContent("console.warnicon.sml").image;
}

public override bool Prepare(GameObject target, Rect availableRect)
{
var isValid = base.Prepare(target, availableRect);
Expand All @@ -224,8 +254,7 @@ public override bool Prepare(GameObject target, Rect availableRect)

isHighlighted = availableRect.Contains(Event.current.mousePosition);

componentIcon = componentIcon != null ? componentIcon : EditorGUIUtility.IconContent("cs Script Icon").image;
transformIcon = transformIcon != null ? transformIcon : EditorGUIUtility.IconContent("Transform Icon").image;
CachePredefinedIcons();
return true;
}

Expand Down Expand Up @@ -256,11 +285,6 @@ public override void OnGui(Rect rect)
for (var i = 1; i < components.Length; i++)
{
var component = components[i];
if (component == null)
{
continue;
}

var content = GetContent(component);
//draw icon for the current component
GUI.Label(iconRect, content);
Expand All @@ -279,8 +303,14 @@ public override void OnGui(Rect rect)
private class HierarchyTreeLinesLabel : HierarchyPropertyLabel, IDisposable
{
private const float firstElementWidthOffset = 4.0f;

#if UNITY_2019_1_OR_NEWER
private const float firstElementXOffset = -45.0f;
private const float startXPosition = 30.0f;
#else
private const float firstElementXOffset = -15.0f;
private const float startXPosition = 0.0f;
#endif
private const float columnSize = 14.0f;

private readonly List<TreeLineLevelRenderer> levelRenderers = new List<TreeLineLevelRenderer>();
Expand Down Expand Up @@ -318,14 +348,15 @@ public sealed override void OnGui(Rect rect)
itemRenderCount++;

rect.x = startXPosition;
rect.width = columnSize + firstElementWidthOffset;
//we need 2x column size for full-line cases when object has no children and there is no foldout
rect.width = 2 * columnSize + firstElementWidthOffset;

var targetTransform = target.transform;
var siblingIndex = targetTransform.GetSiblingIndex();

if (levels > levelRenderers.Count)
{
//Initialize missing tree line level render
//initialize missing tree line level render
var startIndex = levelRenderers.Count;
int x;
for (x = startIndex; x < levels; x++)
Expand Down Expand Up @@ -382,54 +413,56 @@ public void Initialize(Transform transform)

public void OnGUI(Rect rect, GameObject target, int siblingIndex, bool isCurrentLevel)
{
//NOTE: currently we are using labels and predefined chars to display tree lines, this is not optimal solution
// since we can't really control width, tickiness and other potential useful properties. Using few chars allow us
// to display dashed lines very easily but replacing it with standard line would a bit harder.
// For now this is ok solution but probably should be replaced with drawing lines using the EditorGUI.DrawRect API,
// in the same way we draw lines in the Inspector

if (isCurrentLevel)
{
var hasChildren = target.transform.childCount > 0;
GUIContent label;
if (GetParentChildCount(target) == (siblingIndex + 1))
{
renderedLastLevelGameobject = true;
EditorGUI.LabelField(rect, Style.treeElementLast, Style.treeElementStyle);
label = hasChildren ? Style.treeElementLastHalf : Style.treeElementLast;
}
else
{
renderedLastLevelGameobject = false;
EditorGUI.LabelField(rect, Style.treeElementCross, Style.treeElementStyle);
label = hasChildren ? Style.treeElementCrossHalf : Style.treeElementCross;
}

EditorGUI.LabelField(rect, label, Style.treeElementStyle);
return;
}
else

if (!renderedLastLevelGameobject)
{
if (!renderedLastLevelGameobject)
{
EditorGUI.LabelField(rect, Style.treeElementPass, Style.treeElementStyle);
}
EditorGUI.LabelField(rect, Style.treeElementPass, Style.treeElementStyle);
}
}

private int GetParentChildCount(Transform transform)
private int GetParentChildCount(GameObject gameObject)
{
var parent = transform.parent;
if (parent != null)
{
return parent.childCount;
}

var scene = transform.gameObject.scene;
return scene.rootCount;
return GetParentChildCount(gameObject.transform);
}

private int GetParentChildCount(GameObject gameObject)
private int GetParentChildCount(Transform transform)
{
var parent = gameObject.transform.parent;
var parent = transform.parent;
if (parent != null)
{
return parent.childCount;
}

var scene = gameObject.scene;
var scene = transform.gameObject.scene;
return scene.rootCount;
}
}
}
#endregion
#endregion

protected static class Style
{
Expand All @@ -442,15 +475,19 @@ protected static class Style
internal static readonly GUIStyle treeElementStyle;

internal static readonly GUIContent treeElementLast;
internal static readonly GUIContent treeElementLastHalf;
internal static readonly GUIContent treeElementCross;
internal static readonly GUIContent treeElementCrossHalf;
internal static readonly GUIContent treeElementPass;

internal static readonly Color characterColor;

static Style()
{
treeElementLast = new GUIContent("└");
treeElementCross = new GUIContent("├");
treeElementLast = new GUIContent("└--");
treeElementLastHalf = new GUIContent("└-");
treeElementCross = new GUIContent("├--");
treeElementCrossHalf = new GUIContent("├-");
treeElementPass = new GUIContent("│");

defaultAlignTextStyle = new GUIStyle(EditorStyles.miniLabel)
Expand Down Expand Up @@ -481,9 +518,10 @@ static Style()
alignment = TextAnchor.UpperRight
#endif
};
treeElementStyle = new GUIStyle(EditorStyles.miniLabel)
treeElementStyle = new GUIStyle(EditorStyles.label)
{
fontSize = 16,
padding = new RectOffset(4, 0, 0, 0),
fontSize = 12,
};

if (!EditorGUIUtility.isProSkin)
Expand Down
4 changes: 2 additions & 2 deletions Assets/Editor Toolbox/Editor/ToolboxEditorHierarchy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ private static void HandleHeaderObject(GameObject gameObject)

#endregion

internal static void CreateAllowedHierarchyContentCallbacks(params HierarchyItemDataType[] items)
internal static void CreatePropertyLabels(params HierarchyItemDataType[] items)
{
foreach (var item in items)
{
Expand All @@ -301,7 +301,7 @@ internal static void CreateAllowedHierarchyContentCallbacks(params HierarchyItem
}
}

internal static void RemoveAllowedHierarchyContentCallbacks()
internal static void RemovePropertyLabels()
{
for (int i = 0; i < propertyLabels.Count; i++)
{
Expand Down
4 changes: 2 additions & 2 deletions Assets/Editor Toolbox/Editor/ToolboxManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ private static void ManageHierarchyCore(IToolboxHierarchySettings settings)
ToolboxEditorHierarchy.ShowSelectionsCount = settings.ShowSelectionsCount;
ToolboxEditorHierarchy.DrawSeparationLines = true;

ToolboxEditorHierarchy.RemoveAllowedHierarchyContentCallbacks();
ToolboxEditorHierarchy.RemovePropertyLabels();

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

ToolboxEditorHierarchy.RepaintHierarchyOverlay();
Expand Down
5 changes: 2 additions & 3 deletions Assets/Editor Toolbox/EditorSettings.asset
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ MonoBehaviour:
useToolboxHierarchy: 1
drawHorizontalLines: 1
showSelectionsCount: 1
rowDataTypes: 000000000100000005000000
rowDataTypes: 00000000010000000500000004000000
useToolboxFolders: 1
largeIconScale: 0.8
smallIconScale: 0.7
Expand Down Expand Up @@ -93,8 +93,7 @@ MonoBehaviour:
- typeReference: Toolbox.Editor.Drawers.RegexValueAttributeDrawer, Toolbox.Editor
listPropertyDrawerHandlers:
- typeReference: Toolbox.Editor.Drawers.ReorderableListAttributeDrawer, Toolbox.Editor
- typeReference: Toolbox.Editor.Drawers.ReorderableListExposedAttributeDrawer,
Toolbox.Editor
- typeReference: Toolbox.Editor.Drawers.ReorderableListExposedAttributeDrawer, Toolbox.Editor
- typeReference: Toolbox.Editor.Drawers.ScrollableItemsAttributeDrawer, Toolbox.Editor
targetTypeDrawerHandlers:
- typeReference: Toolbox.Editor.Drawers.SerializedDictionaryDrawer, Toolbox.Editor
Loading

0 comments on commit d816c95

Please sign in to comment.