AssetListTree.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using UnityEditor;
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. using UnityEditor.IMGUI.Controls;
  5. using System.Linq;
  6. //using System;
  7. namespace AssetBundleBrowser
  8. {
  9. internal class AssetListTree : TreeView
  10. {
  11. List<AssetBundleModel.BundleInfo> m_SourceBundles = new List<AssetBundleModel.BundleInfo>();
  12. AssetBundleManageTab m_Controller;
  13. List<UnityEngine.Object> m_EmptyObjectList = new List<UnityEngine.Object>();
  14. internal static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState()
  15. {
  16. return new MultiColumnHeaderState(GetColumns());
  17. }
  18. private static MultiColumnHeaderState.Column[] GetColumns()
  19. {
  20. var retVal = new MultiColumnHeaderState.Column[] {
  21. new MultiColumnHeaderState.Column(),
  22. new MultiColumnHeaderState.Column(),
  23. new MultiColumnHeaderState.Column(),
  24. new MultiColumnHeaderState.Column()
  25. };
  26. retVal[0].headerContent = new GUIContent("Asset", "Short name of asset. For full name select asset and see message below");
  27. retVal[0].minWidth = 50;
  28. retVal[0].width = 100;
  29. retVal[0].maxWidth = 300;
  30. retVal[0].headerTextAlignment = TextAlignment.Left;
  31. retVal[0].canSort = true;
  32. retVal[0].autoResize = true;
  33. retVal[1].headerContent = new GUIContent("Bundle", "Bundle name. 'auto' means asset was pulled in due to dependency");
  34. retVal[1].minWidth = 50;
  35. retVal[1].width = 100;
  36. retVal[1].maxWidth = 300;
  37. retVal[1].headerTextAlignment = TextAlignment.Left;
  38. retVal[1].canSort = true;
  39. retVal[1].autoResize = true;
  40. retVal[2].headerContent = new GUIContent("Size", "Size on disk");
  41. retVal[2].minWidth = 30;
  42. retVal[2].width = 75;
  43. retVal[2].maxWidth = 100;
  44. retVal[2].headerTextAlignment = TextAlignment.Left;
  45. retVal[2].canSort = true;
  46. retVal[2].autoResize = true;
  47. retVal[3].headerContent = new GUIContent("!", "Errors, Warnings, or Info");
  48. retVal[3].minWidth = 16;
  49. retVal[3].width = 16;
  50. retVal[3].maxWidth = 16;
  51. retVal[3].headerTextAlignment = TextAlignment.Left;
  52. retVal[3].canSort = true;
  53. retVal[3].autoResize = false;
  54. return retVal;
  55. }
  56. enum MyColumns
  57. {
  58. Asset,
  59. Bundle,
  60. Size,
  61. Message
  62. }
  63. internal enum SortOption
  64. {
  65. Asset,
  66. Bundle,
  67. Size,
  68. Message
  69. }
  70. SortOption[] m_SortOptions =
  71. {
  72. SortOption.Asset,
  73. SortOption.Bundle,
  74. SortOption.Size,
  75. SortOption.Message
  76. };
  77. internal AssetListTree(TreeViewState state, MultiColumnHeaderState mchs, AssetBundleManageTab ctrl ) : base(state, new MultiColumnHeader(mchs))
  78. {
  79. m_Controller = ctrl;
  80. showBorder = true;
  81. showAlternatingRowBackgrounds = true;
  82. multiColumnHeader.sortingChanged += OnSortingChanged;
  83. }
  84. internal void Update()
  85. {
  86. bool dirty = false;
  87. foreach (var bundle in m_SourceBundles)
  88. {
  89. dirty |= bundle.dirty;
  90. }
  91. if (dirty)
  92. Reload();
  93. }
  94. public override void OnGUI(Rect rect)
  95. {
  96. base.OnGUI(rect);
  97. if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition))
  98. {
  99. SetSelection(new int[0], TreeViewSelectionOptions.FireSelectionChanged);
  100. }
  101. }
  102. protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
  103. {
  104. var rows = base.BuildRows(root);
  105. SortIfNeeded(root, rows);
  106. return rows;
  107. }
  108. internal void SetSelectedBundles(IEnumerable<AssetBundleModel.BundleInfo> bundles)
  109. {
  110. m_Controller.SetSelectedItems(null);
  111. m_SourceBundles = bundles.ToList();
  112. SetSelection(new List<int>());
  113. Reload();
  114. }
  115. protected override TreeViewItem BuildRoot()
  116. {
  117. var root = AssetBundleModel.Model.CreateAssetListTreeView(m_SourceBundles);
  118. return root;
  119. }
  120. protected override void RowGUI(RowGUIArgs args)
  121. {
  122. for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
  123. CellGUI(args.GetCellRect(i), args.item as AssetBundleModel.AssetTreeItem, args.GetColumn(i), ref args);
  124. }
  125. private void CellGUI(Rect cellRect, AssetBundleModel.AssetTreeItem item, int column, ref RowGUIArgs args)
  126. {
  127. Color oldColor = GUI.color;
  128. CenterRectUsingSingleLineHeight(ref cellRect);
  129. if(column != 3)
  130. GUI.color = item.itemColor;
  131. switch (column)
  132. {
  133. case 0:
  134. {
  135. var iconRect = new Rect(cellRect.x + 1, cellRect.y + 1, cellRect.height - 2, cellRect.height - 2);
  136. if(item.icon != null)
  137. GUI.DrawTexture(iconRect, item.icon, ScaleMode.ScaleToFit);
  138. DefaultGUI.Label(
  139. new Rect(cellRect.x + iconRect.xMax + 1, cellRect.y, cellRect.width - iconRect.width, cellRect.height),
  140. item.displayName,
  141. args.selected,
  142. args.focused);
  143. }
  144. break;
  145. case 1:
  146. DefaultGUI.Label(cellRect, item.asset.bundleName, args.selected, args.focused);
  147. break;
  148. case 2:
  149. DefaultGUI.Label(cellRect, item.asset.GetSizeString(), args.selected, args.focused);
  150. break;
  151. case 3:
  152. var icon = item.MessageIcon();
  153. if (icon != null)
  154. {
  155. var iconRect = new Rect(cellRect.x, cellRect.y, cellRect.height, cellRect.height);
  156. GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit);
  157. }
  158. break;
  159. }
  160. GUI.color = oldColor;
  161. }
  162. protected override void DoubleClickedItem(int id)
  163. {
  164. var assetItem = FindItem(id, rootItem) as AssetBundleModel.AssetTreeItem;
  165. if (assetItem != null)
  166. {
  167. Object o = AssetDatabase.LoadAssetAtPath<Object>(assetItem.asset.fullAssetName);
  168. EditorGUIUtility.PingObject(o);
  169. Selection.activeObject = o;
  170. }
  171. }
  172. protected override void SelectionChanged(IList<int> selectedIds)
  173. {
  174. if (selectedIds == null)
  175. return;
  176. List<Object> selectedObjects = new List<Object>();
  177. List<AssetBundleModel.AssetInfo> selectedAssets = new List<AssetBundleModel.AssetInfo>();
  178. foreach (var id in selectedIds)
  179. {
  180. var assetItem = FindItem(id, rootItem) as AssetBundleModel.AssetTreeItem;
  181. if (assetItem != null)
  182. {
  183. Object o = AssetDatabase.LoadAssetAtPath<Object>(assetItem.asset.fullAssetName);
  184. selectedObjects.Add(o);
  185. Selection.activeObject = o;
  186. selectedAssets.Add(assetItem.asset);
  187. }
  188. }
  189. m_Controller.SetSelectedItems(selectedAssets);
  190. Selection.objects = selectedObjects.ToArray();
  191. }
  192. protected override bool CanBeParent(TreeViewItem item)
  193. {
  194. return false;
  195. }
  196. protected override bool CanStartDrag(CanStartDragArgs args)
  197. {
  198. args.draggedItemIDs = GetSelection();
  199. return true;
  200. }
  201. protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
  202. {
  203. DragAndDrop.PrepareStartDrag();
  204. DragAndDrop.objectReferences = m_EmptyObjectList.ToArray();
  205. List<AssetBundleModel.AssetTreeItem> items =
  206. new List<AssetBundleModel.AssetTreeItem>(args.draggedItemIDs.Select(id => FindItem(id, rootItem) as AssetBundleModel.AssetTreeItem));
  207. DragAndDrop.paths = items.Select(a => a.asset.fullAssetName).ToArray();
  208. DragAndDrop.SetGenericData("AssetListTreeSource", this);
  209. DragAndDrop.StartDrag("AssetListTree");
  210. }
  211. protected override DragAndDropVisualMode HandleDragAndDrop(DragAndDropArgs args)
  212. {
  213. if(IsValidDragDrop())
  214. {
  215. if (args.performDrop)
  216. {
  217. AssetBundleModel.Model.MoveAssetToBundle(DragAndDrop.paths, m_SourceBundles[0].m_Name.bundleName, m_SourceBundles[0].m_Name.variant);
  218. AssetBundleModel.Model.ExecuteAssetMove();
  219. foreach (var bundle in m_SourceBundles)
  220. {
  221. bundle.RefreshAssetList();
  222. }
  223. m_Controller.UpdateSelectedBundles(m_SourceBundles);
  224. }
  225. return DragAndDropVisualMode.Copy;//Move;
  226. }
  227. return DragAndDropVisualMode.Rejected;
  228. }
  229. protected bool IsValidDragDrop()
  230. {
  231. //can't do drag & drop if data source is read only
  232. if (AssetBundleModel.Model.DataSource.IsReadOnly ())
  233. return false;
  234. //can't drag onto none or >1 bundles
  235. if (m_SourceBundles.Count == 0 || m_SourceBundles.Count > 1)
  236. return false;
  237. //can't drag nothing
  238. if (DragAndDrop.paths == null || DragAndDrop.paths.Length == 0)
  239. return false;
  240. //can't drag into a folder
  241. var folder = m_SourceBundles[0] as AssetBundleModel.BundleFolderInfo;
  242. if (folder != null)
  243. return false;
  244. var data = m_SourceBundles[0] as AssetBundleModel.BundleDataInfo;
  245. if(data == null)
  246. return false; // this should never happen.
  247. var thing = DragAndDrop.GetGenericData("AssetListTreeSource") as AssetListTree;
  248. if (thing != null)
  249. return false;
  250. if(data.IsEmpty())
  251. return true;
  252. if (data.isSceneBundle)
  253. {
  254. foreach (var assetPath in DragAndDrop.paths)
  255. {
  256. if ((AssetDatabase.GetMainAssetTypeAtPath(assetPath) != typeof(SceneAsset)) &&
  257. (!AssetDatabase.IsValidFolder(assetPath)))
  258. return false;
  259. }
  260. }
  261. else
  262. {
  263. foreach (var assetPath in DragAndDrop.paths)
  264. {
  265. if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(SceneAsset))
  266. return false;
  267. }
  268. }
  269. return true;
  270. }
  271. protected override void ContextClickedItem(int id)
  272. {
  273. if (AssetBundleModel.Model.DataSource.IsReadOnly ()) {
  274. return;
  275. }
  276. List<AssetBundleModel.AssetTreeItem> selectedNodes = new List<AssetBundleModel.AssetTreeItem>();
  277. foreach(var nodeID in GetSelection())
  278. {
  279. selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.AssetTreeItem);
  280. }
  281. if(selectedNodes.Count > 0)
  282. {
  283. GenericMenu menu = new GenericMenu();
  284. menu.AddItem(new GUIContent("Remove asset(s) from bundle."), false, RemoveAssets, selectedNodes);
  285. menu.ShowAsContext();
  286. }
  287. }
  288. void RemoveAssets(object obj)
  289. {
  290. var selectedNodes = obj as List<AssetBundleModel.AssetTreeItem>;
  291. var assets = new List<AssetBundleModel.AssetInfo>();
  292. //var bundles = new List<AssetBundleModel.BundleInfo>();
  293. foreach (var node in selectedNodes)
  294. {
  295. if (!System.String.IsNullOrEmpty(node.asset.bundleName))
  296. assets.Add(node.asset);
  297. }
  298. AssetBundleModel.Model.MoveAssetToBundle(assets, string.Empty, string.Empty);
  299. AssetBundleModel.Model.ExecuteAssetMove();
  300. foreach (var bundle in m_SourceBundles)
  301. {
  302. bundle.RefreshAssetList();
  303. }
  304. m_Controller.UpdateSelectedBundles(m_SourceBundles);
  305. //ReloadAndSelect(new List<int>());
  306. }
  307. protected override void KeyEvent()
  308. {
  309. if (m_SourceBundles.Count > 0 && Event.current.keyCode == KeyCode.Delete && GetSelection().Count > 0)
  310. {
  311. List<AssetBundleModel.AssetTreeItem> selectedNodes = new List<AssetBundleModel.AssetTreeItem>();
  312. foreach (var nodeID in GetSelection())
  313. {
  314. selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.AssetTreeItem);
  315. }
  316. RemoveAssets(selectedNodes);
  317. }
  318. }
  319. void OnSortingChanged(MultiColumnHeader multiColumnHeader)
  320. {
  321. SortIfNeeded(rootItem, GetRows());
  322. }
  323. void SortIfNeeded(TreeViewItem root, IList<TreeViewItem> rows)
  324. {
  325. if (rows.Count <= 1)
  326. return;
  327. if (multiColumnHeader.sortedColumnIndex == -1)
  328. return;
  329. SortByColumn();
  330. rows.Clear();
  331. for (int i = 0; i < root.children.Count; i++)
  332. rows.Add(root.children[i]);
  333. Repaint();
  334. }
  335. void SortByColumn()
  336. {
  337. var sortedColumns = multiColumnHeader.state.sortedColumns;
  338. if (sortedColumns.Length == 0)
  339. return;
  340. List<AssetBundleModel.AssetTreeItem> assetList = new List<AssetBundleModel.AssetTreeItem>();
  341. foreach(var item in rootItem.children)
  342. {
  343. assetList.Add(item as AssetBundleModel.AssetTreeItem);
  344. }
  345. var orderedItems = InitialOrder(assetList, sortedColumns);
  346. rootItem.children = orderedItems.Cast<TreeViewItem>().ToList();
  347. }
  348. IOrderedEnumerable<AssetBundleModel.AssetTreeItem> InitialOrder(IEnumerable<AssetBundleModel.AssetTreeItem> myTypes, int[] columnList)
  349. {
  350. SortOption sortOption = m_SortOptions[columnList[0]];
  351. bool ascending = multiColumnHeader.IsSortedAscending(columnList[0]);
  352. switch (sortOption)
  353. {
  354. case SortOption.Asset:
  355. return myTypes.Order(l => l.displayName, ascending);
  356. case SortOption.Size:
  357. return myTypes.Order(l => l.asset.fileSize, ascending);
  358. case SortOption.Message:
  359. return myTypes.Order(l => l.HighestMessageLevel(), ascending);
  360. case SortOption.Bundle:
  361. default:
  362. return myTypes.Order(l => l.asset.bundleName, ascending);
  363. }
  364. }
  365. private void ReloadAndSelect(IList<int> hashCodes)
  366. {
  367. Reload();
  368. SetSelection(hashCodes);
  369. SelectionChanged(hashCodes);
  370. }
  371. }
  372. static class MyExtensionMethods
  373. {
  374. internal static IOrderedEnumerable<T> Order<T, TKey>(this IEnumerable<T> source, System.Func<T, TKey> selector, bool ascending)
  375. {
  376. if (ascending)
  377. {
  378. return source.OrderBy(selector);
  379. }
  380. else
  381. {
  382. return source.OrderByDescending(selector);
  383. }
  384. }
  385. internal static IOrderedEnumerable<T> ThenBy<T, TKey>(this IOrderedEnumerable<T> source, System.Func<T, TKey> selector, bool ascending)
  386. {
  387. if (ascending)
  388. {
  389. return source.ThenBy(selector);
  390. }
  391. else
  392. {
  393. return source.ThenByDescending(selector);
  394. }
  395. }
  396. }
  397. }