unity/EditorPlugin/Unity.2019.2/FindUsages/Window/FindUsagesTreeView.cs (204 lines of code) (raw):
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace JetBrains.Rider.Unity.Editor.FindUsages.Window
{
internal class FindUsagesTreeView : TreeView
{
private readonly Dictionary<int, FindUsagesTreeViewItem> myFindResultItems = new Dictionary<int, FindUsagesTreeViewItem>();
private readonly Dictionary<int, int> myAnimatorItemIdToPathElementsCount = new Dictionary<int, int>();
private readonly FindUsagesWindowTreeState myState;
public FindUsagesTreeView(FindUsagesWindowTreeState state) : base(state)
{
myState = state;
Reload();
}
protected override TreeViewItem BuildRoot()
{
var root = new FindUsagePathElement(0) {id = 0, depth = -1, displayName = "Usages result:"};
var sceneNode = CreateSceneSubTree();
root.AddChild(sceneNode);
var prefabNode = CreatePrefabSubTree();
root.AddChild(prefabNode);
var scriptableObjectNode = CreateScriptableObjectSubTree();
root.AddChild(scriptableObjectNode);
var animatorSubTree = CreateAnimatorSubTree();
root.AddChild(animatorSubTree);
var animationSubTree = CreateAnimationEventsSubTree();
root.AddChild(animationSubTree);
SetupDepthsFromParentsAndChildren(root);
return root;
}
private TreeViewItem CreateSceneSubTree()
{
var scenes = new FindUsagePathElement(0) {id = 1, displayName = "Scenes"};
CreateSubTree(scenes, myState.SceneElements.ToArray(), 50_000);
return scenes;
}
private TreeViewItem CreatePrefabSubTree()
{
var prefabs = new FindUsagePathElement(1) {id = 2, displayName = "Prefabs"};
CreateSubTree(prefabs, myState.PrefabElements.ToArray(), 100_000);
return prefabs;
}
private TreeViewItem CreateScriptableObjectSubTree()
{
var scriptableObject = new FindUsagePathElement(2) {id = 3, displayName = "Scriptable Objects"};
var startId = 150_000;
foreach (var usageElement in myState.ScriptableObjectElements.ToArray())
{
var id = startId++;
myFindResultItems[id] = new FindUsagesTreeViewItem(-1, usageElement)
{
id = id,
displayName = usageElement.FilePath,
icon = (Texture2D) EditorGUIUtility.IconContent(usageElement.TerminalNodeImage).image
};
scriptableObject.AddChild(myFindResultItems[id]);
}
return scriptableObject;
}
private TreeViewItem CreateAnimatorSubTree()
{
var animator = new FindUsagePathElement(3) {id = 4, displayName = "Animator"};
var startId = 200_000;
foreach (var animatorElement in myState.AnimatorElements.ToArray())
{
CreateAnimatorSubTree(animator, animatorElement, ref startId);
}
return animator;
}
private void CreateAnimatorSubTree([NotNull] FindUsagePathElement findUsagePathElement,
[NotNull] AnimatorElement animatorElement, ref int id)
{
var currentParent = findUsagePathElement;
for (int i = 0, pathElementsLength = animatorElement.PathElements.Length; i < pathElementsLength; i++)
{
var icon = i == pathElementsLength - 1
? animatorElement.TerminalNodeImage
: AnimatorElement.AnimatorStateMachineIcon;
var findUsagesTreeViewItem = new FindUsagesTreeViewItem(id, animatorElement)
{
id = id,
displayName = animatorElement.PathElements[i],
icon = (Texture2D) EditorGUIUtility.IconContent(icon).image
};
myFindResultItems[id] = findUsagesTreeViewItem;
myAnimatorItemIdToPathElementsCount[id] = i + 1;
currentParent.AddChild(findUsagesTreeViewItem);
currentParent = findUsagesTreeViewItem;
id++;
}
}
private TreeViewItem CreateAnimationEventsSubTree()
{
var animationTreeRoot = new FindUsagePathElement(4) {id = 5, displayName = "Animations"};
var startId = 250_000;
foreach (var animationEventElement in myState.AnimationElements.ToArray())
{
CreateAnimationEventItem(animationTreeRoot, animationEventElement, ref startId);
}
return animationTreeRoot;
}
private void CreateAnimationEventItem([NotNull] TreeViewItem animationTreeRoot,
[NotNull] AbstractUsageElement animationElement,
ref int id)
{
var findUsagesTreeViewItem = new FindUsagesTreeViewItem(id, animationElement)
{
id = id,
displayName = animationElement.FileName,
icon = (Texture2D) EditorGUIUtility.IconContent(animationElement.TerminalNodeImage)?.image
};
myFindResultItems[id] = findUsagesTreeViewItem;
animationTreeRoot.AddChild(findUsagesTreeViewItem);
id++;
}
private void CreateSubTree(FindUsagePathElement element, IEnumerable<AbstractUsageElement> data, int startId)
{
var fileNames = new Dictionary<string, FindUsagePathElement>();
var curFileNameId = 0;
foreach (var usageElement in data)
{
FindUsagePathElement current = element;
var filePath = usageElement.FilePath;
var dataFileName = filePath.Split('/').Last();
if (!fileNames.ContainsKey(filePath))
{
current = current.CreateChild(new FindUsagePathElement(curFileNameId++)
{
id = startId++, displayName = dataFileName,
icon = (Texture2D)EditorGUIUtility.IconContent(usageElement.StartNodeImage).image
});
fileNames.Add(filePath, current);
}
else
{
current = fileNames[filePath];
}
var pathLength = usageElement.Path.Length;
for (int i = 0; i < pathLength; i++)
{
var name = usageElement.Path[i];
if (i + 1 == pathLength)
{
var id = startId++;
myFindResultItems[id] = new FindUsagesTreeViewItem(usageElement.RootIndices[i], usageElement)
{
id = id,
displayName = name,
icon = (Texture2D) EditorGUIUtility.IconContent(usageElement.TerminalNodeImage).image
};
current.AddChild(myFindResultItems[id]);
}
else
{
var rootIndex = usageElement.RootIndices[i];
if (!current.HasChild(rootIndex))
{
current = current.CreateChild(new FindUsagePathElement(rootIndex)
{
id = startId++, displayName = name,
icon = (Texture2D)EditorGUIUtility.IconContent(usageElement.NodeImage).image
});
}
else
{
current = current.GetChild(rootIndex);
}
}
}
}
}
protected override void DoubleClickedItem(int id)
{
if (!myFindResultItems.ContainsKey(id))
{
SetExpanded(id, true);
return;
}
var request = myFindResultItems[id].UsageElement;
switch (request)
{
case SceneElement sceneElement:
ShowUtil.ShowUsageOnScene(sceneElement.FilePath, sceneElement.FileName, sceneElement.Path, sceneElement.RootIndices);
break;
case PrefabElement _:
case ScriptableObjectElement _:
ShowUtil.ShowFileUsage(request.FilePath);
break;
case AnimatorElement animatorElement:
var elements = animatorElement.PathElements;
var range = elements.ToList().GetRange(0, myAnimatorItemIdToPathElementsCount[id]);
ShowUtil.ShowAnimatorUsage(range.ToArray(), animatorElement.FilePath);
break;
case AnimationElement animationElement:
ShowUtil.ShowAnimationEventUsage(animationElement.FilePath);
break;
}
}
}
}