AssetListTree.cs 16 KB

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