PreferencesGUI.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using UnityEditor;
  7. using UnityEditorInternal;
  8. using UnityEngine;
  9. using Random = UnityEngine.Random;
  10. namespace EnhancedHierarchy {
  11. public static partial class Preferences {
  12. public const string DEVELOPER_EMAIL = "samuelschultze@gmail.com";
  13. public static Action onResetPreferences = new Action(() => { });
  14. public static readonly List<GUIContent> contents = new List<GUIContent>();
  15. public static readonly Version pluginVersion = new Version(2, 4, 5);
  16. public static readonly DateTime pluginDate = new DateTime(2020, 05, 04);
  17. private static readonly GUIContent resetSettingsContent = new GUIContent("Use Defaults", "Reset all settings to default");
  18. private static readonly GUIContent unlockAllContent = new GUIContent("Unlock All Objects", "Unlock all objects in the current scene, it's highly recommended to do this when disabling or removing the extension to prevent data loss\nThis might take a few seconds on large scenes");
  19. private static readonly GUIContent mailDeveloperContent = new GUIContent("Support Email", "Request support from the developer\n\n" + DEVELOPER_EMAIL);
  20. private static readonly GUIContent versionContent = new GUIContent(string.Format("Version: {0} ({1:d})", pluginVersion, pluginDate));
  21. private static Vector2 scroll;
  22. private static ReorderableList leftIconsList, rightIconsList, rowColorsList;
  23. private static readonly string[] minilabelsNames;
  24. private static GenericMenu LeftIconsMenu { get { return GetGenericMenuForIcons(LeftIcons, IconBase.AllLeftIcons); } }
  25. private static GenericMenu RightIconsMenu { get { return GetGenericMenuForIcons(RightIcons, IconBase.AllRightIcons); } }
  26. private static GenericMenu RowColorsMenu {
  27. get {
  28. var menu = new GenericMenu();
  29. var randomColor = Random.ColorHSV(0f, 1f, 0.5f, 1f, 1f, 1f);
  30. randomColor.a = 0.3019608f;
  31. for (var i = 0; i < 32; i++) {
  32. if (PerLayerRowColors.Value.Contains(new LayerColor(i)))
  33. continue;
  34. var mode = PerLayerRowColors.Value.LastOrDefault().mode;
  35. var layerName = LayerMask.LayerToName(i);
  36. var layer = new LayerColor(i, randomColor, mode);
  37. if (string.IsNullOrEmpty(layerName))
  38. layerName = string.Format("Layer: {0}", i);
  39. menu.AddItem(new GUIContent(layerName), false, () => {
  40. rowColorsList.list.Add(layer);
  41. PerLayerRowColors.ForceSave();
  42. });
  43. }
  44. return menu;
  45. }
  46. }
  47. private static GenericMenu GetGenericMenuForIcons<T>(PrefItem<T> preferenceItem, IconBase[] icons)where T : IList {
  48. var menu = new GenericMenu();
  49. foreach (var i in icons) {
  50. var icon = i;
  51. if (!preferenceItem.Value.Contains(icon) && icon != IconBase.none && icon != IconBase.none)
  52. menu.AddItem(new GUIContent(icon.Name), false, () => {
  53. preferenceItem.Value.Add(icon);
  54. preferenceItem.ForceSave();
  55. });
  56. }
  57. return menu;
  58. }
  59. private static ReorderableList GenerateReordableList<T>(PrefItem<T> preferenceItem)where T : IList {
  60. var result = new ReorderableList(preferenceItem.Value, typeof(T), true, true, true, true);
  61. result.elementHeight = 18f;
  62. result.drawHeaderCallback = rect => { rect.xMin -= EditorGUI.indentLevel * 16f; EditorGUI.LabelField(rect, preferenceItem, EditorStyles.boldLabel); };
  63. result.onChangedCallback += list => preferenceItem.ForceSave();
  64. result.drawElementCallback = (rect, index, focused, active) => {
  65. var icon = result.list[index] as IconBase;
  66. if (icon == null) {
  67. EditorGUI.LabelField(rect, "INVALID ICON");
  68. return;
  69. }
  70. var content = Utility.GetTempGUIContent(icon.Name, icon.PreferencesTooltip, icon.PreferencesPreview);
  71. var whiteTexture = content.image ? content.image.name.Contains("eh_icon_white") : true;
  72. using(new GUIColor((whiteTexture && !EditorGUIUtility.isProSkin) ? Styles.backgroundColorEnabled : Color.white))
  73. EditorGUI.LabelField(rect, content);
  74. };
  75. onResetPreferences += () => result.list = preferenceItem.Value;
  76. return result;
  77. }
  78. #if UNITY_2018_3_OR_NEWER
  79. [SettingsProvider]
  80. private static SettingsProvider RetrieveSettingsProvider() {
  81. var settingsProvider = new SettingsProvider("Preferences/Enhanced Hierarchy", SettingsScope.User, contents.Select(c => c.text));
  82. settingsProvider.guiHandler = new Action<string>((str) => OnPreferencesGUI(str));
  83. return settingsProvider;
  84. }
  85. #else
  86. [PreferenceItem("Hierarchy")]
  87. private static void OnPreferencesGUI() {
  88. OnPreferencesGUI(string.Empty);
  89. }
  90. #endif
  91. private static void OnPreferencesGUI(string search) {
  92. scroll = EditorGUILayout.BeginScrollView(scroll, false, false);
  93. EditorGUILayout.Separator();
  94. Enabled.DoGUI();
  95. EditorGUILayout.Separator();
  96. EditorGUILayout.HelpBox("Each item has a tooltip explaining what it does, keep the mouse over it to see.", MessageType.Info);
  97. EditorGUILayout.Separator();
  98. using(Enabled.GetEnabledScope()) {
  99. using(new GUIIndent("Misc settings")) {
  100. using(new GUIIndent("Margins")) {
  101. RightMargin.DoGUISlider(-50, 50);
  102. using(new GUIEnabled(Reflected.HierarchyArea.Supported)) {
  103. LeftMargin.DoGUISlider(-50, 50);
  104. Indent.DoGUISlider(0, 35);
  105. }
  106. if (!Reflected.HierarchyArea.Supported)
  107. EditorGUILayout.HelpBox("Custom Indent and Margins are not supported in this Unity version", MessageType.Warning);
  108. }
  109. IconsSize.DoGUISlider(13, 23);
  110. TreeOpacity.DoGUISlider(0f, 1f);
  111. using(new GUIIndent()) {
  112. using(SelectOnTree.GetFadeScope(TreeOpacity.Value > 0.01f))
  113. SelectOnTree.DoGUI();
  114. using(TreeStemProportion.GetFadeScope(TreeOpacity.Value > 0.01f))
  115. TreeStemProportion.DoGUISlider(0f, 1f);
  116. }
  117. Tooltips.DoGUI();
  118. using(new GUIIndent())
  119. using(RelevantTooltipsOnly.GetFadeScope(Tooltips))
  120. RelevantTooltipsOnly.DoGUI();
  121. if (EnhancedSelectionSupported)
  122. EnhancedSelection.DoGUI();
  123. Trailing.DoGUI();
  124. ChangeAllSelected.DoGUI();
  125. NumericChildExpand.DoGUI();
  126. using(new GUIEnabled(Reflected.IconWidthSupported))
  127. DisableNativeIcon.DoGUI();
  128. using(HideDefaultIcon.GetFadeScope(IsButtonEnabled(new Icons.GameObjectIcon())))
  129. HideDefaultIcon.DoGUI();
  130. using(OpenScriptsOfLogs.GetFadeScope(IsButtonEnabled(new Icons.Warnings())))
  131. OpenScriptsOfLogs.DoGUI();
  132. GUI.changed = false;
  133. using(AllowSelectingLockedObjects.GetFadeScope(IsButtonEnabled(new Icons.Lock())))
  134. AllowSelectingLockedObjects.DoGUI();
  135. #if !UNITY_2019_3_OR_NEWER
  136. using(new GUIEnabled(false))
  137. #endif
  138. using(AllowPickingLockedObjects.GetFadeScope(IsButtonEnabled(new Icons.Lock())))
  139. AllowPickingLockedObjects.DoGUI();
  140. HoverTintColor.DoGUI();
  141. }
  142. using(new GUIIndent("Row separators")) {
  143. LineSize.DoGUISlider(0, 6);
  144. using(LineColor.GetFadeScope(LineSize > 0))
  145. LineColor.DoGUI();
  146. OddRowColor.DoGUI();
  147. EvenRowColor.DoGUI();
  148. GUI.changed = false;
  149. var rect = EditorGUILayout.GetControlRect(false, rowColorsList.GetHeight());
  150. rect.xMin += EditorGUI.indentLevel * 16f;
  151. rowColorsList.DoList(rect);
  152. }
  153. GUI.changed = false;
  154. MiniLabels.Value[0] = EditorGUILayout.Popup("Mini Label Top", MiniLabels.Value[0], minilabelsNames);
  155. MiniLabels.Value[1] = EditorGUILayout.Popup("Mini Label Bottom", MiniLabels.Value[1], minilabelsNames);
  156. if (GUI.changed) {
  157. MiniLabels.ForceSave();
  158. RecreateMiniLabelProviders();
  159. }
  160. // MiniLabel.DoGUI();
  161. using(new GUIIndent()) {
  162. using(SmallerMiniLabel.GetFadeScope(miniLabelProviders.Length > 0))
  163. SmallerMiniLabel.DoGUI();
  164. using(HideDefaultTag.GetFadeScope(MiniLabelTagEnabled))
  165. HideDefaultTag.DoGUI();
  166. using(HideDefaultLayer.GetFadeScope(MiniLabelLayerEnabled))
  167. HideDefaultLayer.DoGUI();
  168. using(CentralizeMiniLabelWhenPossible.GetFadeScope(miniLabelProviders.Length >= 2))
  169. CentralizeMiniLabelWhenPossible.DoGUI();
  170. }
  171. LeftSideButtonPref.DoGUI();
  172. using(new GUIIndent())
  173. using(LeftmostButton.GetFadeScope(LeftSideButton != IconBase.none))
  174. LeftmostButton.DoGUI();
  175. using(new GUIIndent("Children behaviour on change")) {
  176. using(LockAskMode.GetFadeScope(IsButtonEnabled(new Icons.Lock())))
  177. LockAskMode.DoGUI();
  178. using(LayerAskMode.GetFadeScope(IsButtonEnabled(new Icons.Layer()) || MiniLabelLayerEnabled))
  179. LayerAskMode.DoGUI();
  180. using(TagAskMode.GetFadeScope(IsButtonEnabled(new Icons.Tag()) || MiniLabelTagEnabled))
  181. TagAskMode.DoGUI();
  182. using(StaticAskMode.GetFadeScope(IsButtonEnabled(new Icons.Static())))
  183. StaticAskMode.DoGUI();
  184. using(IconAskMode.GetFadeScope(IsButtonEnabled(new Icons.GameObjectIcon())))
  185. IconAskMode.DoGUI();
  186. EditorGUILayout.HelpBox(string.Format("Pressing down {0} while clicking on a button will make it temporary have the opposite children change mode", Utility.CtrlKey), MessageType.Info);
  187. }
  188. leftIconsList.displayAdd = LeftIconsMenu.GetItemCount() > 0;
  189. leftIconsList.DoLayoutList();
  190. rightIconsList.displayAdd = RightIconsMenu.GetItemCount() > 0;
  191. rightIconsList.DoLayoutList();
  192. EditorGUILayout.HelpBox("Alt + Click on child expand toggle makes it expand all the grandchildren too", MessageType.Info);
  193. if (IsButtonEnabled(new Icons.Memory()))
  194. EditorGUILayout.HelpBox("\"Memory Used\" may create garbage and consequently framerate stutterings, leave it disabled if maximum performance is important for your project", MessageType.Warning);
  195. if (IsButtonEnabled(new Icons.Lock()))
  196. EditorGUILayout.HelpBox("Remember to always unlock your game objects when removing or disabling this extension, as you won't be able to unlock without it and may lose scene data", MessageType.Warning);
  197. GUI.enabled = true;
  198. EditorGUILayout.EndScrollView();
  199. using(new EditorGUILayout.HorizontalScope()) {
  200. GUILayout.FlexibleSpace();
  201. EditorGUILayout.LabelField(versionContent, GUILayout.Width(170f));
  202. }
  203. using(new EditorGUILayout.HorizontalScope()) {
  204. if (GUILayout.Button(resetSettingsContent, GUILayout.Width(120f)))
  205. onResetPreferences();
  206. // if (GUILayout.Button(unlockAllContent, GUILayout.Width(120f)))
  207. // Utility.UnlockAllObjects();
  208. if (GUILayout.Button(mailDeveloperContent, GUILayout.Width(120f)))
  209. OpenSupportEmail();
  210. }
  211. EditorGUILayout.Separator();
  212. Styles.ReloadTooltips();
  213. EditorApplication.RepaintHierarchyWindow();
  214. }
  215. }
  216. public static void OpenSupportEmail(Exception e = null) {
  217. Application.OpenURL(GetEmailURL(e));
  218. }
  219. private static string GetEmailURL(Exception e) {
  220. var full = new StringBuilder();
  221. var body = new StringBuilder();
  222. #if UNITY_2018_1_OR_NEWER
  223. Func<string, string> EscapeURL = url => { return UnityEngine.Networking.UnityWebRequest.EscapeURL(url).Replace("+", "%20"); };
  224. #else
  225. Func<string, string> EscapeURL = url => { return WWW.EscapeURL(url).Replace("+", "%20"); };
  226. #endif
  227. body.Append(EscapeURL("\r\nDescribe your problem or make your request here\r\n"));
  228. body.Append(EscapeURL("\r\nAdditional Information:"));
  229. body.Append(EscapeURL("\r\nVersion: " + pluginVersion.ToString(3)));
  230. body.Append(EscapeURL("\r\nUnity " + InternalEditorUtility.GetFullUnityVersion()));
  231. body.Append(EscapeURL("\r\n" + SystemInfo.operatingSystem));
  232. if (e != null)
  233. body.Append(EscapeURL("\r\n" + e));
  234. full.Append("mailto:");
  235. full.Append(DEVELOPER_EMAIL);
  236. full.Append("?subject=");
  237. full.Append(EscapeURL("Enhanced Hierarchy - Support"));
  238. full.Append("&body=");
  239. full.Append(body);
  240. return full.ToString();
  241. }
  242. private static LayerColor LayerColorField(Rect rect, LayerColor layerColor) {
  243. var value = layerColor;
  244. var rect1 = rect;
  245. var rect2 = rect;
  246. var rect3 = rect;
  247. var rect4 = rect;
  248. rect1.xMax = rect1.xMin + 175f;
  249. rect2.xMin = rect1.xMax;
  250. rect2.xMax = rect2.xMin + 80f;
  251. rect3.xMin = rect2.xMax;
  252. rect3.xMax = rect3.xMin + 100;
  253. rect4.xMin = rect3.xMax;
  254. value.layer = EditorGUI.LayerField(rect1, value.layer);
  255. value.layer = EditorGUI.DelayedIntField(rect2, value.layer);
  256. value.color = EditorGUI.ColorField(rect3, value.color);
  257. value.mode = (TintMode)EditorGUI.EnumPopup(rect4, value.mode);
  258. if (value.layer > 31 || value.layer < 0)
  259. return layerColor;
  260. return value;
  261. }
  262. private static void DoGUI(this PrefItem<int> prefItem) {
  263. if (prefItem.Drawing)
  264. prefItem.Value = EditorGUILayout.IntField(prefItem, prefItem);
  265. }
  266. private static void DoGUI(this PrefItem<float> prefItem) {
  267. if (prefItem.Drawing)
  268. prefItem.Value = EditorGUILayout.FloatField(prefItem, prefItem);
  269. }
  270. private static void DoGUISlider(this PrefItem<int> prefItem, int min, int max) {
  271. if (prefItem.Drawing)
  272. prefItem.Value = EditorGUILayout.IntSlider(prefItem, prefItem, min, max);
  273. }
  274. private static void DoGUISlider(this PrefItem<float> prefItem, float min, float max) {
  275. if (prefItem.Drawing)
  276. prefItem.Value = EditorGUILayout.Slider(prefItem, prefItem, min, max);
  277. }
  278. private static void DoGUI(this PrefItem<bool> prefItem) {
  279. if (prefItem.Drawing)
  280. prefItem.Value = EditorGUILayout.Toggle(prefItem, prefItem);
  281. }
  282. private static void DoGUI(this PrefItem<string> prefItem) {
  283. if (prefItem.Drawing)
  284. prefItem.Value = EditorGUILayout.TextField(prefItem.Label, prefItem);
  285. }
  286. private static void DoGUI(this PrefItem<Color> prefItem) {
  287. if (prefItem.Drawing)
  288. prefItem.Value = EditorGUILayout.ColorField(prefItem, prefItem);
  289. }
  290. private static void DoGUI<T>(this PrefItem<T> prefItem)where T : struct, IConvertible {
  291. if (prefItem.Drawing)
  292. prefItem.Value = (T)(object)EditorGUILayout.EnumPopup(prefItem, (Enum)(object)prefItem.Value);
  293. }
  294. private static void DoGUI(this PrefItem<IconData> prefItem) {
  295. if (!prefItem.Drawing)
  296. return;
  297. var icons = IconBase.AllLeftOfNameIcons;
  298. var index = Array.IndexOf(icons, prefItem.Value.Icon);
  299. var labels = (from icon in icons select new GUIContent(icon)).ToArray();
  300. index = EditorGUILayout.Popup(prefItem, index, labels);
  301. if (index < 0 || index >= icons.Length)
  302. return;
  303. if (prefItem.Value.Icon.Name == icons[index].Name)
  304. return;
  305. prefItem.Value.Icon = icons[index];
  306. prefItem.ForceSave();
  307. }
  308. }
  309. }