Reflected.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. using System;
  2. using System.Linq;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using Object = UnityEngine.Object;
  6. namespace EnhancedHierarchy {
  7. public static partial class Reflected {
  8. private static bool gameObjectStylesTypeLoaded = false;
  9. private static Type gameObjectTreeViewStylesType;
  10. private static readonly Type hierarchyWindowType = ReflectionHelper.FindType("UnityEditor.SceneHierarchyWindow");
  11. private static EditorWindow hierarchyWindowInstance;
  12. public static bool HierarchyFocused {
  13. get {
  14. return EditorWindow.focusedWindow && EditorWindow.focusedWindow.GetType() == hierarchyWindowType;
  15. }
  16. }
  17. public static Color PlaymodeTint {
  18. get {
  19. try {
  20. return !EditorApplication.isPlayingOrWillChangePlaymode ?
  21. Color.white :
  22. ReflectionHelper.FindType("UnityEditor.HostView").GetStaticField<object>("kPlayModeDarken").GetInstanceProperty<Color>("Color");
  23. } catch (Exception e) {
  24. if (Preferences.DebugEnabled)
  25. Debug.LogException(e);
  26. return Color.white;
  27. }
  28. }
  29. }
  30. public static EditorWindow HierarchyWindowInstance {
  31. get {
  32. if (hierarchyWindowInstance)
  33. return hierarchyWindowInstance;
  34. var lastHierarchy = (EditorWindow)null;
  35. try {
  36. lastHierarchy = hierarchyWindowType.GetStaticField<EditorWindow>("s_LastInteractedHierarchy");
  37. } catch (Exception e) {
  38. if (Preferences.DebugEnabled)
  39. Debug.LogException(e);
  40. }
  41. return lastHierarchy != null ?
  42. (hierarchyWindowInstance = lastHierarchy) :
  43. (hierarchyWindowInstance = (EditorWindow)Resources.FindObjectsOfTypeAll(hierarchyWindowType).FirstOrDefault());
  44. }
  45. }
  46. public static void ShowIconSelector(Object[] targetObjs, Rect activatorRect, bool showLabelIcons) {
  47. using(ProfilerSample.Get())
  48. try {
  49. var iconSelectorType = ReflectionHelper.FindType("UnityEditor.IconSelector");
  50. if (iconSelectorType.HasMethod<Object[], Rect, bool>("ShowAtPosition")) {
  51. if (!iconSelectorType.InvokeStaticMethod<bool, Object[], Rect, bool>("ShowAtPosition", targetObjs, activatorRect, showLabelIcons))
  52. Debug.LogWarning("Failed to open icon selector");
  53. return;
  54. } else {
  55. var instance = ScriptableObject.CreateInstance(iconSelectorType);
  56. if (instance.HasMethod<Object[], Rect, bool>("Init"))
  57. instance.InvokeMethod("Init", targetObjs, activatorRect, showLabelIcons);
  58. else {
  59. var affectedObj = targetObjs.FirstOrDefault();
  60. instance.InvokeMethod("Init", affectedObj, activatorRect, showLabelIcons);
  61. After.Condition(() => !instance, () => {
  62. var icon = GetObjectIcon(affectedObj);
  63. foreach (var obj in targetObjs)
  64. SetObjectIcon(obj, icon);
  65. });
  66. }
  67. }
  68. } catch (Exception e) {
  69. Debug.LogWarning("Failed to open icon selector\n" + e);
  70. }
  71. }
  72. public static void SetObjectIcon(Object obj, Texture2D texture) {
  73. using(ProfilerSample.Get()) {
  74. typeof(EditorGUIUtility).InvokeStaticMethod("SetIconForObject", obj, texture);
  75. EditorUtility.SetDirty(obj);
  76. }
  77. }
  78. public static Texture2D GetObjectIcon(Object obj) {
  79. using(ProfilerSample.Get())
  80. return typeof(EditorGUIUtility).InvokeStaticMethod<Texture2D, Object>("GetIconForObject", obj);
  81. }
  82. public static bool GetTransformIsExpanded(GameObject go) {
  83. using(ProfilerSample.Get())
  84. try {
  85. var data = TreeView.GetInstanceProperty<object>("data");
  86. var isExpanded = data.InvokeMethod<bool, int>("IsExpanded", go.GetInstanceID());
  87. return isExpanded;
  88. } catch (Exception e) {
  89. Preferences.NumericChildExpand.Value = false;
  90. Debug.LogException(e);
  91. Debug.LogWarningFormat("Disabled \"{0}\" because it failed to get hierarchy info", Preferences.NumericChildExpand.Label.text);
  92. return false;
  93. }
  94. }
  95. public static void SetHierarchySelectionNeedSync() {
  96. using(ProfilerSample.Get())
  97. try {
  98. if (HierarchyWindowInstance)
  99. SceneHierarchyOrWindow.SetInstanceProperty("selectionSyncNeeded", true);
  100. } catch (Exception e) {
  101. Debug.LogWarningFormat("Enabling \"{0}\" because it caused an exception", Preferences.AllowSelectingLockedObjects.Label.text);
  102. Debug.LogException(e);
  103. Preferences.AllowSelectingLockedObjects.Value = true;
  104. }
  105. }
  106. private static object SceneHierarchy { get { return HierarchyWindowInstance.GetInstanceProperty<object>("sceneHierarchy"); } }
  107. private static object SceneHierarchyOrWindow {
  108. get {
  109. #if UNITY_2018_3_OR_NEWER
  110. return HierarchyWindowInstance.GetInstanceProperty<object>("sceneHierarchy");
  111. #else
  112. return HierarchyWindowInstance;
  113. #endif
  114. }
  115. }
  116. public static object TreeView { get { return SceneHierarchyOrWindow.GetInstanceProperty<object>("treeView"); } }
  117. public static object TreeViewGUI { get { return TreeView.GetInstanceProperty<object>("gui"); } }
  118. public static bool IconWidthSupported {
  119. get {
  120. #if UNITY_2018_3_OR_NEWER
  121. return TreeView != null && TreeViewGUI != null && TreeViewGUI.HasField("k_IconWidth");
  122. #else
  123. return false;
  124. #endif
  125. }
  126. }
  127. // Icon to the left side of obj name, introducted on Unity 2018.3
  128. public static float IconWidth {
  129. get {
  130. if (!IconWidthSupported)
  131. return 0;
  132. return TreeViewGUI.GetInstanceField<float>("k_IconWidth");
  133. }
  134. set { TreeViewGUI.SetInstanceField("k_IconWidth", value); }
  135. }
  136. public static class HierarchyArea {
  137. static HierarchyArea() {
  138. if (Preferences.DebugEnabled && !Supported)
  139. Debug.LogWarning("HierarchyArea not supported!");
  140. }
  141. public static bool Supported {
  142. get {
  143. try {
  144. return HierarchyWindowInstance != null && TreeView != null && TreeViewGUI != null;
  145. } catch {
  146. return false;
  147. }
  148. }
  149. }
  150. public static float IndentWidth {
  151. get { return TreeViewGUI.GetInstanceField<float>("k_IndentWidth"); }
  152. set { TreeViewGUI.SetInstanceField("k_IndentWidth", value); }
  153. }
  154. //public static float foldoutYOffset {
  155. // get { return TreeViewGUI.GetFieldValue<float>("foldoutYOffset"); }
  156. // set { TreeViewGUI.SetFieldValue("foldoutYOffset", value); }
  157. //}
  158. private static float baseIndentDefault = float.NaN;
  159. public static float BaseIndent {
  160. get {
  161. var val = TreeViewGUI.GetInstanceField<float>("k_BaseIndent");
  162. if (float.IsNaN(baseIndentDefault))
  163. baseIndentDefault = val;
  164. return val;
  165. }
  166. set {
  167. if (float.IsNaN(baseIndentDefault))
  168. baseIndentDefault = BaseIndent;
  169. TreeViewGUI.SetInstanceField("k_BaseIndent", baseIndentDefault + value);
  170. }
  171. }
  172. public static float BottomRowMargin {
  173. get { return TreeViewGUI.GetInstanceField<float>("k_BottomRowMargin"); }
  174. set { TreeViewGUI.SetInstanceField("k_BottomRowMargin", value); }
  175. }
  176. public static float TopRowMargin {
  177. get { return TreeViewGUI.GetInstanceField<float>("k_TopRowMargin"); }
  178. set { TreeViewGUI.SetInstanceField("k_TopRowMargin", value); }
  179. }
  180. public static float HalfDropBetweenHeight {
  181. get { return TreeViewGUI.GetInstanceField<float>("k_HalfDropBetweenHeight"); }
  182. set { TreeViewGUI.SetInstanceField("k_HalfDropBetweenHeight", value); }
  183. }
  184. public static float IconWidth {
  185. get { return TreeViewGUI.GetInstanceField<float>("k_IconWidth"); }
  186. set { TreeViewGUI.SetInstanceField("k_IconWidth", value); }
  187. }
  188. public static float LineHeight {
  189. get { return TreeViewGUI.GetInstanceField<float>("k_LineHeight"); }
  190. set { TreeViewGUI.SetInstanceField("k_LineHeight", value); }
  191. }
  192. public static float SpaceBetweenIconAndText {
  193. get { return TreeViewGUI.GetInstanceField<float>("k_SpaceBetweenIconAndText"); }
  194. set { TreeViewGUI.SetInstanceField("k_SpaceBetweenIconAndText", value); }
  195. }
  196. public static float IconLeftPadding {
  197. get { return TreeViewGUI.GetInstanceProperty<float>("iconLeftPadding"); }
  198. set { TreeViewGUI.SetInstanceProperty("iconLeftPadding", value); }
  199. }
  200. public static float IconRightPadding { //Same as iconLeftPadding
  201. get { return TreeViewGUI.GetInstanceProperty<float>("iconRightPadding"); }
  202. set { TreeViewGUI.SetInstanceProperty("iconRightPadding", value); }
  203. }
  204. }
  205. private static Type GameObjectTreeViewStylesType {
  206. get {
  207. if (!gameObjectStylesTypeLoaded) {
  208. gameObjectStylesTypeLoaded = true;
  209. gameObjectTreeViewStylesType = TreeViewGUI.GetType().GetNestedType("GameObjectStyles", ReflectionHelper.FULL_BINDING);
  210. }
  211. return gameObjectTreeViewStylesType;
  212. }
  213. }
  214. public static bool NativeHierarchyHoverTintSupported {
  215. get {
  216. return GameObjectTreeViewStylesType != null && GameObjectTreeViewStylesType.HasField("hoveredBackgroundColor");
  217. }
  218. }
  219. // I implement the hover tint and then a few weeks later
  220. // unity implements it as a native feature
  221. public static Color NativeHierarchyHoverTint {
  222. get {
  223. if (Preferences.DebugEnabled && !NativeHierarchyHoverTintSupported) {
  224. Debug.LogWarning("Native hover tint not supported!");
  225. return Color.clear;
  226. }
  227. return GameObjectTreeViewStylesType.GetStaticField<Color>("hoveredBackgroundColor");
  228. }
  229. set {
  230. if (Preferences.DebugEnabled && !NativeHierarchyHoverTintSupported) {
  231. Debug.LogWarning("Native hover tint not supported!");
  232. return;
  233. }
  234. GameObjectTreeViewStylesType.SetStaticField("hoveredBackgroundColor", value);
  235. }
  236. }
  237. }
  238. }