AssetSettingsAnalyzeTreeView.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.AddressableAssets.Build;
  5. using UnityEditor.AddressableAssets.Build.AnalyzeRules;
  6. using UnityEditor.AddressableAssets.Settings;
  7. using UnityEditor.IMGUI.Controls;
  8. using UnityEngine;
  9. using Debug = UnityEngine.Debug;
  10. using Object = UnityEngine.Object;
  11. namespace UnityEditor.AddressableAssets.GUI
  12. {
  13. class AssetSettingsAnalyzeTreeView : TreeView
  14. {
  15. private int m_CurrentDepth;
  16. internal AssetSettingsAnalyzeTreeView(TreeViewState state)
  17. : base(state)
  18. {
  19. showAlternatingRowBackgrounds = true;
  20. showBorder = true;
  21. Reload();
  22. }
  23. private List<AnalyzeRuleContainerTreeViewItem> GatherAllInheritRuleContainers(TreeViewItem baseContainer)
  24. {
  25. List<AnalyzeRuleContainerTreeViewItem> retValue = new List<AnalyzeRuleContainerTreeViewItem>();
  26. if (!baseContainer.hasChildren)
  27. return new List<AnalyzeRuleContainerTreeViewItem>();
  28. foreach (var child in baseContainer.children)
  29. {
  30. if (child is AnalyzeRuleContainerTreeViewItem)
  31. {
  32. retValue.AddRange(GatherAllInheritRuleContainers(child as AnalyzeRuleContainerTreeViewItem));
  33. retValue.Add(child as AnalyzeRuleContainerTreeViewItem);
  34. }
  35. }
  36. return retValue;
  37. }
  38. private void PerformActionForEntireRuleSelection(Action<AnalyzeRuleContainerTreeViewItem> action)
  39. {
  40. List<AnalyzeRuleContainerTreeViewItem> activeSelection = (from id in GetSelection()
  41. let selection = FindItem(id, rootItem)
  42. where selection is AnalyzeRuleContainerTreeViewItem
  43. select selection as AnalyzeRuleContainerTreeViewItem).ToList();
  44. List<AnalyzeRuleContainerTreeViewItem> inheritSelection = new List<AnalyzeRuleContainerTreeViewItem>();
  45. foreach (var selected in activeSelection)
  46. inheritSelection.AddRange(GatherAllInheritRuleContainers(selected));
  47. List<AnalyzeRuleContainerTreeViewItem> entireSelection = activeSelection.Union(inheritSelection).ToList();
  48. foreach (AnalyzeRuleContainerTreeViewItem ruleContainer in entireSelection)
  49. {
  50. if (ruleContainer.analyzeRule != null)
  51. {
  52. action(ruleContainer);
  53. }
  54. }
  55. }
  56. public void RunAllSelectedRules()
  57. {
  58. PerformActionForEntireRuleSelection((ruleContainer) =>
  59. {
  60. var results = AnalyzeSystem.RefreshAnalysis(ruleContainer.analyzeRule);
  61. BuildResults(ruleContainer, results);
  62. Reload();
  63. UpdateSelections(GetSelection());
  64. });
  65. }
  66. public void FixAllSelectedRules()
  67. {
  68. PerformActionForEntireRuleSelection((ruleContainer) =>
  69. {
  70. AnalyzeSystem.FixIssues(ruleContainer.analyzeRule);
  71. var results = AnalyzeSystem.RefreshAnalysis(ruleContainer.analyzeRule);
  72. BuildResults(ruleContainer, results);
  73. Reload();
  74. UpdateSelections(GetSelection());
  75. });
  76. }
  77. public void ClearAllSelectedRules()
  78. {
  79. PerformActionForEntireRuleSelection((ruleContainer) =>
  80. {
  81. AnalyzeSystem.ClearAnalysis(ruleContainer.analyzeRule);
  82. BuildResults(ruleContainer, new List<AnalyzeRule.AnalyzeResult>());
  83. Reload();
  84. UpdateSelections(GetSelection());
  85. });
  86. }
  87. public bool SelectionContainsFixableRule { get; private set; }
  88. public bool SelectionContainsRuleContainer { get; private set; }
  89. public bool SelectionContainsErrors { get; private set; }
  90. protected override void SelectionChanged(IList<int> selectedIds)
  91. {
  92. UpdateSelections(selectedIds);
  93. }
  94. void UpdateSelections(IList<int> selectedIds)
  95. {
  96. var allSelectedRuleContainers = (from id in selectedIds
  97. let ruleContainer = FindItem(id, rootItem) as AnalyzeRuleContainerTreeViewItem
  98. where ruleContainer != null
  99. select ruleContainer);
  100. List<AnalyzeRuleContainerTreeViewItem> allRuleContainers = new List<AnalyzeRuleContainerTreeViewItem>();
  101. foreach (var ruleContainer in allSelectedRuleContainers)
  102. {
  103. allRuleContainers.AddRange(GatherAllInheritRuleContainers(ruleContainer));
  104. allRuleContainers.Add(ruleContainer);
  105. }
  106. allRuleContainers = allRuleContainers.Distinct().ToList();
  107. SelectionContainsErrors = (from container in allRuleContainers
  108. from child in container.children
  109. where child is AnalyzeResultsTreeViewItem && (child as AnalyzeResultsTreeViewItem).IsError
  110. select child).Any();
  111. SelectionContainsRuleContainer = allRuleContainers.Any();
  112. SelectionContainsFixableRule = (from container in allRuleContainers
  113. where container.analyzeRule.CanFix
  114. select container).Any();
  115. }
  116. protected override void ContextClicked()
  117. {
  118. if (SelectionContainsRuleContainer)
  119. {
  120. GenericMenu menu = new GenericMenu();
  121. menu.AddItem(new GUIContent("Run Analyze Rule"), false, RunAllSelectedRules);
  122. menu.AddItem(new GUIContent("Clear Analyze Results"), false, ClearAllSelectedRules);
  123. if (SelectionContainsFixableRule && SelectionContainsErrors)
  124. menu.AddItem(new GUIContent("Fix Analyze Rule"), false, FixAllSelectedRules);
  125. else
  126. menu.AddDisabledItem(new GUIContent("Fix Analyze Rule"));
  127. IList<int> selectedIds = GetSelection();
  128. if (selectedIds.Count == 1)
  129. {
  130. AnalyzeRuleContainerTreeViewItem analyzeRuleContainer = FindItem(selectedIds[0], rootItem) as AnalyzeRuleContainerTreeViewItem;
  131. if (analyzeRuleContainer != null)
  132. {
  133. foreach (var customMenuItem in analyzeRuleContainer.analyzeRule.GetCustomContextMenuItems())
  134. {
  135. if(customMenuItem.MenuEnabled)
  136. menu.AddItem(new GUIContent(customMenuItem.MenuName), customMenuItem.ToggledOn, () => customMenuItem.MenuAction());
  137. else
  138. menu.AddDisabledItem(new GUIContent(customMenuItem.MenuName));
  139. }
  140. }
  141. }
  142. menu.ShowAsContext();
  143. Repaint();
  144. }
  145. else
  146. {
  147. var selectedIds = this.GetSelection();
  148. List<AnalyzeResultsTreeViewItem> items = new List<AnalyzeResultsTreeViewItem>();
  149. foreach (int id in selectedIds)
  150. {
  151. var item = FindItem(id, rootItem) as AnalyzeResultsTreeViewItem;
  152. if (item != null)
  153. items.Add(item);
  154. }
  155. if (items.Count > 0)
  156. AnalyzeResultsTreeViewItem.ContextClicked(items);
  157. }
  158. }
  159. protected override void DoubleClickedItem(int id)
  160. {
  161. var item = FindItem(id, rootItem) as AnalyzeResultsTreeViewItem;
  162. if (item != null)
  163. item.DoubleClicked();
  164. }
  165. protected override TreeViewItem BuildRoot()
  166. {
  167. m_CurrentDepth = 0;
  168. var root = new TreeViewItem(-1, -1);
  169. root.children = new List<TreeViewItem>();
  170. string baseName = "Analyze Rules";
  171. string fixableRules = "Fixable Rules";
  172. string unfixableRules = "Unfixable Rules";
  173. AnalyzeSystem.TreeView = this;
  174. AnalyzeRuleContainerTreeViewItem baseViewItem = new AnalyzeRuleContainerTreeViewItem(baseName.GetHashCode(), m_CurrentDepth, baseName);
  175. baseViewItem.children = new List<TreeViewItem>();
  176. baseViewItem.analyzeRule.CanFix = true;
  177. root.AddChild(baseViewItem);
  178. m_CurrentDepth++;
  179. var fixable = new AnalyzeRuleContainerTreeViewItem(fixableRules.GetHashCode(), m_CurrentDepth, fixableRules);
  180. var unfixable = new AnalyzeRuleContainerTreeViewItem(unfixableRules.GetHashCode(), m_CurrentDepth, unfixableRules);
  181. fixable.analyzeRule.CanFix = true;
  182. unfixable.analyzeRule.CanFix = false;
  183. baseViewItem.AddChild(fixable);
  184. baseViewItem.AddChild(unfixable);
  185. m_CurrentDepth++;
  186. for (int i = 0; i < AnalyzeSystem.Rules.Count; i++)
  187. {
  188. AnalyzeRuleContainerTreeViewItem ruleContainer = new AnalyzeRuleContainerTreeViewItem(
  189. AnalyzeSystem.Rules[i].ruleName.GetHashCode(), m_CurrentDepth, AnalyzeSystem.Rules[i]);
  190. if (ruleContainer.analyzeRule.CanFix)
  191. fixable.AddChild(ruleContainer);
  192. else
  193. unfixable.AddChild(ruleContainer);
  194. }
  195. m_CurrentDepth++;
  196. int index = 0;
  197. var ruleContainers = GatherAllInheritRuleContainers(baseViewItem);
  198. foreach (var ruleContainer in ruleContainers)
  199. {
  200. if(ruleContainer == null)
  201. continue;
  202. EditorUtility.DisplayProgressBar("Calculating Analyze Results...", ruleContainer.displayName, (index / (float)ruleContainers.Count));
  203. if (AnalyzeSystem.AnalyzeData.Data.ContainsKey(ruleContainer.analyzeRule.ruleName))
  204. BuildResults(ruleContainer, AnalyzeSystem.AnalyzeData.Data[ruleContainer.analyzeRule.ruleName]);
  205. index++;
  206. }
  207. EditorUtility.ClearProgressBar();
  208. return root;
  209. }
  210. private readonly Dictionary<int, AnalyzeResultsTreeViewItem> hashToAnalyzeResults = new Dictionary<int, AnalyzeResultsTreeViewItem>();
  211. void BuildResults(TreeViewItem root, List<AnalyzeRule.AnalyzeResult> ruleResults)
  212. {
  213. hashToAnalyzeResults.Clear();
  214. int updateFrequency = Mathf.Max(ruleResults.Count / 10, 1);
  215. for (int index=0; index < ruleResults.Count; ++index)
  216. {
  217. var result = ruleResults[index];
  218. if (index == 0 || index % updateFrequency == 0)
  219. EditorUtility.DisplayProgressBar("Building Results Tree...", result.resultName, (float)index / hashToAnalyzeResults.Keys.Count);
  220. var resPath = result.resultName.Split(AnalyzeRule.kDelimiter);
  221. string name = string.Empty;
  222. TreeViewItem parent = root;
  223. for (int i = 0; i < resPath.Length; i++)
  224. {
  225. name += resPath[i];
  226. int hash = name.GetHashCode();
  227. if (!hashToAnalyzeResults.ContainsKey(hash))
  228. {
  229. AnalyzeResultsTreeViewItem item = new AnalyzeResultsTreeViewItem(hash, i + m_CurrentDepth, resPath[i], result.severity, result);
  230. hashToAnalyzeResults.Add(item.id, item);
  231. parent.AddChild(item);
  232. parent = item;
  233. }
  234. else
  235. {
  236. var targetItem = hashToAnalyzeResults[hash];
  237. targetItem.results.Add(result);
  238. parent = targetItem;
  239. }
  240. }
  241. }
  242. EditorUtility.ClearProgressBar();
  243. List<TreeViewItem> allTreeViewItems = new List<TreeViewItem>();
  244. allTreeViewItems.Add(root);
  245. allTreeViewItems.AddRange(root.children);
  246. foreach (var node in allTreeViewItems)
  247. (node as AnalyzeTreeViewItemBase)?.AddIssueCountToName();
  248. AnalyzeSystem.SerializeData();
  249. }
  250. protected override void RowGUI(RowGUIArgs args)
  251. {
  252. var item = args.item as AnalyzeResultsTreeViewItem;
  253. if (item != null && item.severity != MessageType.None)
  254. {
  255. Texture2D icon = null;
  256. switch (item.severity)
  257. {
  258. case MessageType.Info:
  259. icon = GetInfoIcon();
  260. break;
  261. case MessageType.Warning:
  262. icon = GetWarningIcon();
  263. break;
  264. case MessageType.Error:
  265. icon = GetErrorIcon();
  266. break;
  267. }
  268. UnityEngine.GUI.Label(
  269. new Rect(args.rowRect.x + baseIndent, args.rowRect.y, args.rowRect.width - baseIndent,
  270. args.rowRect.height), new GUIContent(icon, string.Empty));
  271. }
  272. base.RowGUI(args);
  273. }
  274. Texture2D m_ErrorIcon;
  275. Texture2D m_WarningIcon;
  276. Texture2D m_InfoIcon;
  277. Texture2D GetErrorIcon()
  278. {
  279. if (m_ErrorIcon == null)
  280. FindMessageIcons();
  281. return m_ErrorIcon;
  282. }
  283. Texture2D GetWarningIcon()
  284. {
  285. if (m_WarningIcon == null)
  286. FindMessageIcons();
  287. return m_WarningIcon;
  288. }
  289. Texture2D GetInfoIcon()
  290. {
  291. if (m_InfoIcon == null)
  292. FindMessageIcons();
  293. return m_InfoIcon;
  294. }
  295. void FindMessageIcons()
  296. {
  297. m_ErrorIcon = EditorGUIUtility.FindTexture("console.errorIcon");
  298. m_WarningIcon = EditorGUIUtility.FindTexture("console.warnicon");
  299. m_InfoIcon = EditorGUIUtility.FindTexture("console.infoIcon");
  300. }
  301. }
  302. class AnalyzeTreeViewItemBase : TreeViewItem
  303. {
  304. private string baseDisplayName;
  305. private string currentDisplayName;
  306. public override string displayName
  307. {
  308. get { return currentDisplayName; }
  309. set { baseDisplayName = value; }
  310. }
  311. public AnalyzeTreeViewItemBase(int id, int depth, string displayName) : base(id, depth,
  312. displayName)
  313. {
  314. currentDisplayName = baseDisplayName = displayName;
  315. }
  316. public int AddIssueCountToName()
  317. {
  318. int issueCount = 0;
  319. if (children != null)
  320. {
  321. foreach (var child in children)
  322. {
  323. var analyzeNode = child as AnalyzeResultsTreeViewItem;
  324. if (analyzeNode != null)
  325. issueCount += analyzeNode.AddIssueCountToName();
  326. }
  327. }
  328. if (issueCount == 0)
  329. return 1;
  330. currentDisplayName = baseDisplayName + " (" + issueCount + ")";
  331. return issueCount;
  332. }
  333. }
  334. class AnalyzeResultsTreeViewItem : AnalyzeTreeViewItemBase
  335. {
  336. public MessageType severity { get; set; }
  337. public HashSet<AnalyzeRule.AnalyzeResult> results { get; }
  338. public bool IsError
  339. {
  340. get { return !displayName.Contains("No issues found"); }
  341. }
  342. public AnalyzeResultsTreeViewItem(int id, int depth, string displayName, MessageType type)
  343. : base(id, depth, displayName)
  344. {
  345. severity = type;
  346. results = new HashSet<AnalyzeRule.AnalyzeResult>();
  347. }
  348. public AnalyzeResultsTreeViewItem(int id, int depth, string displayName, MessageType type, AnalyzeRule.AnalyzeResult analyzeResult)
  349. : base(id, depth, displayName)
  350. {
  351. severity = type;
  352. results = new HashSet<AnalyzeRule.AnalyzeResult>() {analyzeResult};
  353. }
  354. internal static void ContextClicked(List<AnalyzeResultsTreeViewItem> items)
  355. {
  356. HashSet<UnityEngine.Object> objects = new HashSet<Object>();
  357. foreach (AnalyzeResultsTreeViewItem viewItem in items)
  358. {
  359. foreach (var itemResult in viewItem.results)
  360. {
  361. Object o = GetResultObject(itemResult.resultName);
  362. if (o != null)
  363. objects.Add(o);
  364. }
  365. }
  366. if (objects.Count > 0)
  367. {
  368. GenericMenu menu = new GenericMenu();
  369. menu.AddItem(new GUIContent(objects.Count > 0 ? "Select Assets" : "Select Asset"), false, () =>
  370. {
  371. Selection.objects = objects.ToArray();
  372. foreach (Object o in objects)
  373. EditorGUIUtility.PingObject(o);
  374. });
  375. menu.ShowAsContext();
  376. }
  377. }
  378. static UnityEngine.Object GetResultObject(string resultName)
  379. {
  380. int li = resultName.LastIndexOf(AnalyzeRule.kDelimiter);
  381. if (li >= 0)
  382. {
  383. string assetPath = resultName.Substring(li + 1);
  384. string guid = AssetDatabase.AssetPathToGUID(assetPath);
  385. if (!string.IsNullOrEmpty(guid))
  386. return AssetDatabase.LoadMainAssetAtPath(assetPath);
  387. }
  388. return null;
  389. }
  390. internal void DoubleClicked()
  391. {
  392. HashSet<UnityEngine.Object> objects = new HashSet<Object>();
  393. foreach (var itemResult in results)
  394. {
  395. Object o = GetResultObject(itemResult.resultName);
  396. if (o != null)
  397. objects.Add(o);
  398. }
  399. if (objects.Count > 0)
  400. {
  401. Selection.objects = objects.ToArray();
  402. foreach (Object o in objects)
  403. EditorGUIUtility.PingObject(o);
  404. }
  405. }
  406. }
  407. class AnalyzeRuleContainerTreeViewItem : AnalyzeTreeViewItemBase
  408. {
  409. internal AnalyzeRule analyzeRule;
  410. public AnalyzeRuleContainerTreeViewItem(int id, int depth, AnalyzeRule rule) : base(id, depth, rule.ruleName)
  411. {
  412. analyzeRule = rule;
  413. children = new List<TreeViewItem>();
  414. }
  415. public AnalyzeRuleContainerTreeViewItem(int id, int depth, string displayName) : base(id, depth, displayName)
  416. {
  417. analyzeRule = new AnalyzeRule();
  418. children = new List<TreeViewItem>();
  419. }
  420. }
  421. }