using UnityEditor; using UnityEngine; using System.Collections.Generic; using UnityEditor.IMGUI.Controls; using System.Linq; using System; using AssetBundleBrowser.AssetBundleModel; namespace AssetBundleBrowser { internal class AssetBundleTree : TreeView { private AssetBundleManageTab m_Controller; private bool m_ContextOnItem = false; private List m_EmptyObjectList = new List(); internal AssetBundleTree(TreeViewState state, AssetBundleManageTab ctrl) : base(state) { AssetBundleModel.Model.Rebuild(); m_Controller = ctrl; showBorder = true; } protected override bool CanMultiSelect(TreeViewItem item) { return true; } protected override bool CanRename(TreeViewItem item) { return item != null && item.displayName.Length > 0; } protected override bool DoesItemMatchSearch(TreeViewItem item, string search) { BundleTreeItem bundleItem = item as AssetBundleModel.BundleTreeItem; return bundleItem.bundle.DoesItemMatchSearch(search); } protected override void RowGUI(RowGUIArgs args) { BundleTreeItem bundleItem = args.item as AssetBundleModel.BundleTreeItem; if (args.item.icon == null) extraSpaceBeforeIconAndLabel = 16f; else extraSpaceBeforeIconAndLabel = 0f; Color old = GUI.color; if (bundleItem.bundle as AssetBundleModel.BundleVariantFolderInfo != null) GUI.color = AssetBundleModel.Model.k_LightGrey; //new Color(0.3f, 0.5f, 0.85f); base.RowGUI(args); GUI.color = old; MessageSystem.Message message = bundleItem.BundleMessage(); if (message.severity != MessageType.None) { float size = args.rowRect.height; float right = args.rowRect.xMax; Rect messageRect = new Rect(right - size, args.rowRect.yMin, size, size); GUI.Label(messageRect, new GUIContent(message.icon, message.message)); } } protected override void RenameEnded(RenameEndedArgs args) { base.RenameEnded(args); if (args.newName.Length > 0 && args.newName != args.originalName) { args.newName = args.newName.ToLower(); args.acceptedRename = true; BundleTreeItem renamedItem = FindItem(args.itemID, rootItem) as AssetBundleModel.BundleTreeItem; args.acceptedRename = AssetBundleModel.Model.HandleBundleRename(renamedItem, args.newName); ReloadAndSelect(renamedItem.bundle.nameHashCode, false); } else { args.acceptedRename = false; } } protected override TreeViewItem BuildRoot() { AssetBundleModel.Model.Refresh(); BundleTreeItem root = AssetBundleModel.Model.CreateBundleTreeView(); return root; } protected override void SelectionChanged(IList selectedIds) { List selectedBundles = new List(); if (selectedIds != null) foreach (int id in selectedIds) { BundleTreeItem item = FindItem(id, rootItem) as AssetBundleModel.BundleTreeItem; if (item != null && item.bundle != null) { item.bundle.RefreshAssetList(); selectedBundles.Add(item.bundle); } } m_Controller.UpdateSelectedBundles(selectedBundles); } public override void OnGUI(Rect rect) { base.OnGUI(rect); if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition)) SetSelection(new int[0], TreeViewSelectionOptions.FireSelectionChanged); } protected override void ContextClicked() { if (m_ContextOnItem) { m_ContextOnItem = false; return; } List selectedNodes = new List(); GenericMenu menu = new GenericMenu(); if (!AssetBundleModel.Model.DataSource.IsReadOnly()) { menu.AddItem(new GUIContent("Add new bundle"), false, CreateNewBundle, selectedNodes); menu.AddItem(new GUIContent("Add new folder"), false, CreateFolder, selectedNodes); } menu.AddItem(new GUIContent("Reload all data"), false, ForceReloadData, selectedNodes); menu.ShowAsContext(); } protected override void ContextClickedItem(int id) { if (AssetBundleModel.Model.DataSource.IsReadOnly()) return; m_ContextOnItem = true; List selectedNodes = new List(); foreach (int nodeID in GetSelection()) selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.BundleTreeItem); GenericMenu menu = new GenericMenu(); if (selectedNodes.Count == 1) { if (selectedNodes[0].bundle as AssetBundleModel.BundleFolderConcreteInfo != null) { menu.AddItem(new GUIContent("Add Child/New Bundle"), false, CreateNewBundle, selectedNodes); menu.AddItem(new GUIContent("Add Child/New Folder"), false, CreateFolder, selectedNodes); menu.AddItem(new GUIContent("Add Sibling/New Bundle"), false, CreateNewSiblingBundle, selectedNodes); menu.AddItem(new GUIContent("Add Sibling/New Folder"), false, CreateNewSiblingFolder, selectedNodes); } else if (selectedNodes[0].bundle as AssetBundleModel.BundleVariantFolderInfo != null) { menu.AddItem(new GUIContent("Add Child/New Variant"), false, CreateNewVariant, selectedNodes); menu.AddItem(new GUIContent("Add Sibling/New Bundle"), false, CreateNewSiblingBundle, selectedNodes); menu.AddItem(new GUIContent("Add Sibling/New Folder"), false, CreateNewSiblingFolder, selectedNodes); } else { BundleVariantDataInfo variant = selectedNodes[0].bundle as AssetBundleModel.BundleVariantDataInfo; if (variant == null) { menu.AddItem(new GUIContent("Add Sibling/New Bundle"), false, CreateNewSiblingBundle, selectedNodes); menu.AddItem(new GUIContent("Add Sibling/New Folder"), false, CreateNewSiblingFolder, selectedNodes); menu.AddItem(new GUIContent("Convert to variant"), false, ConvertToVariant, selectedNodes); } else { menu.AddItem(new GUIContent("Add Sibling/New Variant"), false, CreateNewSiblingVariant, selectedNodes); } } if (selectedNodes[0].bundle.IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles)) menu.AddItem(new GUIContent("Move duplicates to new bundle"), false, DedupeAllBundles, selectedNodes); menu.AddItem(new GUIContent("Rename"), false, RenameBundle, selectedNodes); menu.AddItem(new GUIContent("Delete " + selectedNodes[0].displayName), false, DeleteBundles, selectedNodes); } else if (selectedNodes.Count > 1) { menu.AddItem(new GUIContent("Move duplicates shared by selected"), false, DedupeOverlappedBundles, selectedNodes); menu.AddItem(new GUIContent("Move duplicates existing in any selected"), false, DedupeAllBundles, selectedNodes); menu.AddItem(new GUIContent("Delete " + selectedNodes.Count + " selected bundles"), false, DeleteBundles, selectedNodes); } menu.ShowAsContext(); } private void ForceReloadData(object context) { AssetBundleModel.Model.ForceReloadData(this); } private void CreateNewSiblingFolder(object context) { List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count > 0) { AssetBundleModel.BundleFolderConcreteInfo folder = null; folder = selectedNodes[0].bundle.parent as AssetBundleModel.BundleFolderConcreteInfo; CreateFolderUnderParent(folder); } else { Debug.LogError("could not add 'sibling' with no bundles selected"); } } private void CreateFolder(object context) { AssetBundleModel.BundleFolderConcreteInfo folder = null; List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count > 0) folder = selectedNodes[0].bundle as AssetBundleModel.BundleFolderConcreteInfo; CreateFolderUnderParent(folder); } private void CreateFolderUnderParent(AssetBundleModel.BundleFolderConcreteInfo folder) { BundleFolderInfo newBundle = AssetBundleModel.Model.CreateEmptyBundleFolder(folder); ReloadAndSelect(newBundle.nameHashCode, true); } private void RenameBundle(object context) { List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count > 0) BeginRename(FindItem(selectedNodes[0].bundle.nameHashCode, rootItem)); } private void CreateNewSiblingBundle(object context) { List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count > 0) { AssetBundleModel.BundleFolderConcreteInfo folder = null; folder = selectedNodes[0].bundle.parent as AssetBundleModel.BundleFolderConcreteInfo; CreateBundleUnderParent(folder); } else { Debug.LogError("could not add 'sibling' with no bundles selected"); } } private void CreateNewBundle(object context) { AssetBundleModel.BundleFolderConcreteInfo folder = null; List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count > 0) folder = selectedNodes[0].bundle as AssetBundleModel.BundleFolderConcreteInfo; CreateBundleUnderParent(folder); } private void CreateBundleUnderParent(AssetBundleModel.BundleFolderInfo folder) { BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyBundle(folder); ReloadAndSelect(newBundle.nameHashCode, true); } private void CreateNewSiblingVariant(object context) { List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count > 0) { AssetBundleModel.BundleVariantFolderInfo folder = null; folder = selectedNodes[0].bundle.parent as AssetBundleModel.BundleVariantFolderInfo; CreateVariantUnderParent(folder); } else { Debug.LogError("could not add 'sibling' with no bundles selected"); } } private void CreateNewVariant(object context) { AssetBundleModel.BundleVariantFolderInfo folder = null; List selectedNodes = context as List; if (selectedNodes != null && selectedNodes.Count == 1) { folder = selectedNodes[0].bundle as AssetBundleModel.BundleVariantFolderInfo; CreateVariantUnderParent(folder); } } private void CreateVariantUnderParent(AssetBundleModel.BundleVariantFolderInfo folder) { if (folder != null) { BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyVariant(folder); ReloadAndSelect(newBundle.nameHashCode, true); } } private void ConvertToVariant(object context) { List selectedNodes = context as List; if (selectedNodes.Count == 1) { BundleDataInfo bundle = selectedNodes[0].bundle as AssetBundleModel.BundleDataInfo; BundleInfo newBundle = AssetBundleModel.Model.HandleConvertToVariant(bundle); int hash = 0; if (newBundle != null) hash = newBundle.nameHashCode; ReloadAndSelect(hash, true); } } private void DedupeOverlappedBundles(object context) { DedupeBundles(context, true); } private void DedupeAllBundles(object context) { DedupeBundles(context, false); } private void DedupeBundles(object context, bool onlyOverlappedAssets) { List selectedNodes = context as List; BundleInfo newBundle = AssetBundleModel.Model.HandleDedupeBundles(selectedNodes.Select(item => item.bundle), onlyOverlappedAssets); if (newBundle != null) { List selection = new List(); selection.Add(newBundle.nameHashCode); ReloadAndSelect(selection); } else { if (onlyOverlappedAssets) Debug.LogWarning("There were no duplicated assets that existed across all selected bundles."); else Debug.LogWarning("No duplicate assets found after refreshing bundle contents."); } } private void DeleteBundles(object b) { List selectedNodes = b as List; AssetBundleModel.Model.HandleBundleDelete(selectedNodes.Select(item => item.bundle)); ReloadAndSelect(new List()); } protected override void KeyEvent() { if (Event.current.keyCode == KeyCode.Delete && GetSelection().Count > 0) { List selectedNodes = new List(); foreach (int nodeID in GetSelection()) selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.BundleTreeItem); DeleteBundles(selectedNodes); } } private class DragAndDropData { internal bool hasBundleFolder = false; internal bool hasScene = false; internal bool hasNonScene = false; internal bool hasVariantChild = false; internal List draggedNodes; internal AssetBundleModel.BundleTreeItem targetNode; internal DragAndDropArgs args; internal string[] paths; internal DragAndDropData(DragAndDropArgs a) { args = a; draggedNodes = DragAndDrop.GetGenericData("AssetBundleModel.BundleInfo") as List; targetNode = args.parentItem as AssetBundleModel.BundleTreeItem; paths = DragAndDrop.paths; if (draggedNodes != null) foreach (BundleInfo bundle in draggedNodes) if (bundle as AssetBundleModel.BundleFolderInfo != null) { hasBundleFolder = true; } else { BundleDataInfo dataBundle = bundle as AssetBundleModel.BundleDataInfo; if (dataBundle != null) { if (dataBundle.isSceneBundle) hasScene = true; else hasNonScene = true; if (dataBundle as AssetBundleModel.BundleVariantDataInfo != null) hasVariantChild = true; } } else if (DragAndDrop.paths != null) foreach (string assetPath in DragAndDrop.paths) if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(SceneAsset)) hasScene = true; else hasNonScene = true; } } protected override DragAndDropVisualMode HandleDragAndDrop(DragAndDropArgs args) { DragAndDropVisualMode visualMode = DragAndDropVisualMode.None; DragAndDropData data = new DragAndDropData(args); if (AssetBundleModel.Model.DataSource.IsReadOnly()) return DragAndDropVisualMode.Rejected; if (data.hasScene && data.hasNonScene || data.hasVariantChild) return DragAndDropVisualMode.Rejected; switch (args.dragAndDropPosition) { case DragAndDropPosition.UponItem: visualMode = HandleDragDropUpon(data); break; case DragAndDropPosition.BetweenItems: visualMode = HandleDragDropBetween(data); break; case DragAndDropPosition.OutsideItems: if (data.draggedNodes != null) { visualMode = DragAndDropVisualMode.Copy; if (data.args.performDrop) { AssetBundleModel.Model.HandleBundleReparent(data.draggedNodes, null); Reload(); } } else if (data.paths != null) { visualMode = DragAndDropVisualMode.Copy; if (data.args.performDrop) DragPathsToNewSpace(data.paths, null); } break; } return visualMode; } private DragAndDropVisualMode HandleDragDropUpon(DragAndDropData data) { DragAndDropVisualMode visualMode = DragAndDropVisualMode.Copy; //Move; BundleDataInfo targetDataBundle = data.targetNode.bundle as AssetBundleModel.BundleDataInfo; if (targetDataBundle != null) { if (targetDataBundle.isSceneBundle) { if (data.hasNonScene) return DragAndDropVisualMode.Rejected; } else { if (data.hasBundleFolder) return DragAndDropVisualMode.Rejected; else if (data.hasScene && !targetDataBundle.IsEmpty()) return DragAndDropVisualMode.Rejected; } if (data.args.performDrop) { if (data.draggedNodes != null) { AssetBundleModel.Model.HandleBundleMerge(data.draggedNodes, targetDataBundle); ReloadAndSelect(targetDataBundle.nameHashCode, false); } else if (data.paths != null) { AssetBundleModel.Model.MoveAssetToBundle(data.paths, targetDataBundle.m_Name.bundleName, targetDataBundle.m_Name.variant); AssetBundleModel.Model.ExecuteAssetMove(); ReloadAndSelect(targetDataBundle.nameHashCode, false); } } } else { BundleFolderInfo folder = data.targetNode.bundle as AssetBundleModel.BundleFolderInfo; if (folder != null) { if (data.args.performDrop) { if (data.draggedNodes != null) { AssetBundleModel.Model.HandleBundleReparent(data.draggedNodes, folder); Reload(); } else if (data.paths != null) { DragPathsToNewSpace(data.paths, folder); } } } else { visualMode = DragAndDropVisualMode.Rejected; //must be a variantfolder } } return visualMode; } private DragAndDropVisualMode HandleDragDropBetween(DragAndDropData data) { DragAndDropVisualMode visualMode = DragAndDropVisualMode.Copy; //Move; BundleTreeItem parent = data.args.parentItem as AssetBundleModel.BundleTreeItem; if (parent != null) { BundleVariantFolderInfo variantFolder = parent.bundle as AssetBundleModel.BundleVariantFolderInfo; if (variantFolder != null) return DragAndDropVisualMode.Rejected; if (data.args.performDrop) { BundleFolderConcreteInfo folder = parent.bundle as AssetBundleModel.BundleFolderConcreteInfo; if (folder != null) { if (data.draggedNodes != null) { AssetBundleModel.Model.HandleBundleReparent(data.draggedNodes, folder); Reload(); } else if (data.paths != null) { DragPathsToNewSpace(data.paths, folder); } } } } return visualMode; } private string[] dragToNewSpacePaths = null; private AssetBundleModel.BundleFolderInfo dragToNewSpaceRoot = null; private void DragPathsAsOneBundle() { BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyBundle(dragToNewSpaceRoot); AssetBundleModel.Model.MoveAssetToBundle(dragToNewSpacePaths, newBundle.m_Name.bundleName, newBundle.m_Name.variant); AssetBundleModel.Model.ExecuteAssetMove(); ReloadAndSelect(newBundle.nameHashCode, true); } private void DragPathsAsManyBundles() { List hashCodes = new List(); foreach (string assetPath in dragToNewSpacePaths) { BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyBundle(dragToNewSpaceRoot, System.IO.Path.GetFileNameWithoutExtension(assetPath).ToLower()); AssetBundleModel.Model.MoveAssetToBundle(assetPath, newBundle.m_Name.bundleName, newBundle.m_Name.variant); hashCodes.Add(newBundle.nameHashCode); } AssetBundleModel.Model.ExecuteAssetMove(); ReloadAndSelect(hashCodes); } private void DragPathsToNewSpace(string[] paths, AssetBundleModel.BundleFolderInfo root) { dragToNewSpacePaths = paths; dragToNewSpaceRoot = root; if (paths.Length > 1) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Create 1 Bundle"), false, DragPathsAsOneBundle); string message = "Create "; message += paths.Length; message += " Bundles"; menu.AddItem(new GUIContent(message), false, DragPathsAsManyBundles); menu.ShowAsContext(); } else { DragPathsAsManyBundles(); } } protected override void SetupDragAndDrop(SetupDragAndDropArgs args) { if (args.draggedItemIDs == null) return; DragAndDrop.PrepareStartDrag(); List selectedBundles = new List(); foreach (int id in args.draggedItemIDs) { BundleTreeItem item = FindItem(id, rootItem) as AssetBundleModel.BundleTreeItem; selectedBundles.Add(item.bundle); } DragAndDrop.paths = null; DragAndDrop.objectReferences = m_EmptyObjectList.ToArray(); DragAndDrop.SetGenericData("AssetBundleModel.BundleInfo", selectedBundles); DragAndDrop.visualMode = DragAndDropVisualMode.Copy; //Move; DragAndDrop.StartDrag("AssetBundleTree"); } protected override bool CanStartDrag(CanStartDragArgs args) { return true; } internal void Refresh() { IList selection = GetSelection(); Reload(); SelectionChanged(selection); } private void ReloadAndSelect(int hashCode, bool rename) { List selection = new List(); selection.Add(hashCode); ReloadAndSelect(selection); if (rename) BeginRename(FindItem(hashCode, rootItem), 0.25f); } private void ReloadAndSelect(IList hashCodes) { Reload(); SetSelection(hashCodes, TreeViewSelectionOptions.RevealAndFrame); SelectionChanged(hashCodes); } } }