Skip to content

Commit a59c46b

Browse files
Lucas Pereira Vasconcelosxsduan
authored andcommitted
Using reflection to set folder icons on the scene hierarchy
1 parent 9e2fcf7 commit a59c46b

File tree

4 files changed

+184
-69
lines changed

4 files changed

+184
-69
lines changed

Editor/HierarchyFolderIcon.cs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Linq.Expressions;
6+
using System.Reflection;
7+
using System.Runtime.Remoting.Messaging;
8+
using System.Xml;
9+
using UnityEditor;
10+
using UnityEditor.IMGUI.Controls;
11+
using UnityEditor.SceneManagement;
12+
using UnityEditorInternal;
13+
using UnityEngine;
14+
using UnityEngine.Experimental.Rendering;
15+
using UnityHierarchyFolders.Runtime;
16+
using Object = UnityEngine.Object;
17+
18+
namespace Plugins.UnityHierarchyFolders
19+
{
20+
public class HierarchyFolderIcon
21+
{
22+
private static Texture2D _openFolderTexture;
23+
private static Texture2D _closedFolderTexture;
24+
private static Texture2D _openFolderSelectedTexture;
25+
private static Texture2D _closedFolderSelectedTexture;
26+
27+
private static bool _isInitialized;
28+
private static bool _hasProcessedFrame;
29+
30+
// Reflected members
31+
private static PropertyInfo _sceneHierarchyProperty;
32+
private static PropertyInfo _treeViewProperty;
33+
private static PropertyInfo _dataProperty;
34+
private static MethodInfo _getRowsMethod;
35+
private static MethodInfo _isExpandedMethod;
36+
private static PropertyInfo _selectedIconProperty;
37+
private static PropertyInfo _objectPPTRProperty;
38+
private static MethodInfo _getAllSceneHierarchyWindowsMethod;
39+
40+
41+
[InitializeOnLoadMethod]
42+
static void Startup()
43+
{
44+
Initialize();
45+
EditorApplication.update += RefreshFolderIcons;
46+
EditorApplication.hierarchyWindowItemOnGUI += RefreshFolderIcons;
47+
}
48+
49+
private static Texture2D GetTintedTexture(Texture2D original)
50+
{
51+
var tinted = new Texture2D(original.width, original.height,
52+
original.graphicsFormat, original.mipmapCount, TextureCreationFlags.MipChain);
53+
54+
Graphics.CopyTexture(original, tinted);
55+
var data = tinted.GetRawTextureData<Color32>();
56+
for (int index = 0, len = data.Length; index < len; index++)
57+
{
58+
Color32 c = data[index];
59+
byte a = c.a;
60+
61+
Color.RGBToHSV(c, out float h, out float s, out float v);
62+
c = Color.HSVToRGB(0, 0, 1);
63+
c.a = a;
64+
65+
data[index] = c;
66+
}
67+
68+
var aaa = tinted.width * tinted.height * 4;
69+
var offset = 0;
70+
for (int index = 0; index < tinted.mipmapCount; index++)
71+
{
72+
tinted.SetPixelData(data, index, offset);
73+
aaa >>= 2;
74+
offset += aaa;
75+
}
76+
tinted.Apply();
77+
78+
return tinted;
79+
}
80+
81+
private static void Initialize()
82+
{
83+
if (_isInitialized)
84+
{
85+
return;
86+
}
87+
88+
_closedFolderTexture = (Texture2D) EditorGUIUtility.IconContent("Folder Icon").image;
89+
_openFolderTexture = (Texture2D) EditorGUIUtility.IconContent("FolderEmpty Icon").image;
90+
91+
_openFolderSelectedTexture = GetTintedTexture(_openFolderTexture);
92+
_closedFolderSelectedTexture = GetTintedTexture(_closedFolderTexture);
93+
94+
const BindingFlags BindingAll = BindingFlags.Public
95+
| BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
96+
97+
//public static List<SceneHierarchyWindow> GetAllSceneHierarchyWindows()
98+
var assembly = typeof(SceneView).Assembly;
99+
100+
var sceneHierarchyWindowType = assembly.GetType("UnityEditor.SceneHierarchyWindow");
101+
_getAllSceneHierarchyWindowsMethod = sceneHierarchyWindowType.GetMethod("GetAllSceneHierarchyWindows", BindingAll);
102+
_sceneHierarchyProperty = sceneHierarchyWindowType.GetProperty("sceneHierarchy");
103+
104+
var sceneHierarchyType = assembly.GetType("UnityEditor.SceneHierarchy");
105+
_treeViewProperty = sceneHierarchyType.GetProperty("treeView", BindingAll);
106+
107+
var treeViewControllerType = assembly.GetType("UnityEditor.IMGUI.Controls.TreeViewController");
108+
_dataProperty = treeViewControllerType.GetProperty("data", BindingAll);
109+
110+
var iTreeViewDataSourceType = assembly.GetType("UnityEditor.IMGUI.Controls.ITreeViewDataSource");
111+
_getRowsMethod = iTreeViewDataSourceType.GetMethod("GetRows");
112+
_isExpandedMethod = iTreeViewDataSourceType.GetMethod("IsExpanded", new Type[] {typeof(TreeViewItem)});
113+
114+
var gameObjectTreeViewItemType = assembly.GetType("UnityEditor.GameObjectTreeViewItem");
115+
_selectedIconProperty = gameObjectTreeViewItemType.GetProperty("selectedIcon", BindingAll);
116+
_objectPPTRProperty = gameObjectTreeViewItemType.GetProperty("objectPPTR", BindingAll);
117+
118+
_isInitialized = true;
119+
}
120+
121+
private static void RefreshFolderIcons()
122+
{
123+
Initialize();
124+
_hasProcessedFrame = false;
125+
}
126+
127+
128+
private static void RefreshFolderIcons(int instanceid, Rect selectionrect)
129+
{
130+
if (_hasProcessedFrame)
131+
{
132+
return;
133+
}
134+
135+
_hasProcessedFrame = true;
136+
137+
var windows = ((IEnumerable)_getAllSceneHierarchyWindowsMethod.Invoke(null, new object[0])).Cast<EditorWindow>().ToList();
138+
foreach (EditorWindow h in windows)
139+
{
140+
var sceneHierarchy = _sceneHierarchyProperty.GetValue(h);
141+
var treeView = _treeViewProperty.GetValue(sceneHierarchy);
142+
var data = _dataProperty.GetValue(treeView);
143+
144+
IList<TreeViewItem> rows = (IList<TreeViewItem>) _getRowsMethod.Invoke(data, new object[0]);
145+
foreach (TreeViewItem item in rows)
146+
{
147+
Object itemObject = (Object) _objectPPTRProperty.GetValue(item);
148+
if (!itemObject || !Folder.folders.Contains(itemObject.GetInstanceID()))
149+
{
150+
continue;
151+
}
152+
153+
var isExpanded = (bool) _isExpandedMethod.Invoke(data, new object[] {item});
154+
155+
item.icon = isExpanded ? _openFolderTexture : _closedFolderTexture;
156+
_selectedIconProperty.SetValue(item, isExpanded ? _openFolderSelectedTexture : _closedFolderSelectedTexture);
157+
}
158+
}
159+
}
160+
}
161+
}

HierarchyFolderIcon.cs

Lines changed: 0 additions & 67 deletions
This file was deleted.

Runtime/Folder.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#if UNITY_EDITOR
22
using System;
33
using System.Collections.Generic;
4+
using System.Collections.ObjectModel;
45
using System.Linq;
56
using UnityEditor;
67
#endif
@@ -36,13 +37,13 @@ internal static bool CanDestroy(this Component t) => !t.gameObject.GetComponents
3637
#endif
3738

3839
[DisallowMultipleComponent]
39-
[ExecuteInEditMode]
40+
[ExecuteAlways]
4041
public class Folder : MonoBehaviour
4142
{
4243
#if UNITY_EDITOR
4344
private static bool addedSelectionResetCallback;
4445

45-
Folder()
46+
private Folder()
4647
{
4748
// add reset callback first in queue
4849
if (!addedSelectionResetCallback)
@@ -56,6 +57,19 @@ public class Folder : MonoBehaviour
5657

5758
private static Tool lastTool;
5859
private static Folder toolLock;
60+
61+
public static HashSet<int> folders = new HashSet<int>();
62+
//public static ReadOnlyCollection<int> folders => new ReadOnlyCollection<int>(_folders.ToList());
63+
64+
private void Start()
65+
{
66+
folders.Add(gameObject.GetInstanceID());
67+
}
68+
69+
private void OnDestroy()
70+
{
71+
folders.Remove(gameObject.GetInstanceID());
72+
}
5973

6074
/// <summary>Hides all gizmos if selected to avoid accidental editing of the transform.</summary>
6175
private void HandleSelection()
@@ -148,6 +162,11 @@ private void Update()
148162
this.transform.localScale = new Vector3(1, 1, 1);
149163

150164
#if UNITY_EDITOR
165+
if (!Application.IsPlaying(gameObject))
166+
{
167+
folders.Add(gameObject.GetInstanceID());
168+
}
169+
151170
this.EnsureExclusiveComponent();
152171
#endif
153172
}
@@ -158,10 +177,12 @@ public void Flatten()
158177
// gather first-level children
159178
foreach (Transform child in this.transform.GetComponentsInChildren<Transform>(includeInactive: true))
160179
{
180+
var index = transform.GetSiblingIndex();
161181
if (child.parent == this.transform)
162182
{
163183
child.name = $"{this.name}/{child.name}";
164184
child.parent = this.transform.parent;
185+
child.SetSiblingIndex(++index);
165186
}
166187
}
167188

0 commit comments

Comments
 (0)