// /*=============================================================================== // Copyright (C) 2020 PhantomsXR Ltd. All Rights Reserved. // // This file is part of the AR-MOD SDK. // // The AR-MOD SDK cannot be copied, distributed, or made available to // third-parties for commercial purposes without written permission of PhantomsXR Ltd. // // Contact info@phantomsxr.com for licensing requests. // ===============================================================================*/ using System; using System.Collections.Generic; using System.IO; using System.Linq; using Phantom.XRMOD.ActionNotification.Runtime; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEngine; using Object = UnityEngine.Object; namespace Phantom.XRMOD.XRMODPackageTools.Editor { public class ContentTreeRenderer : TreeView { const float CONST_ROW_HEIGHTS = 20f; public Action addedNewElement; public Action removedElement; public bool CanDragAndDrop = true; private AllProjectsCache allProjectsCache; private List contentModels; enum ColumnsType { Icon1, Name, AssetPath, } public ContentTreeRenderer(TreeViewState state) : base(state) { } public ContentTreeRenderer(TreeViewState state, MultiColumnHeader multiColumnHeader, AllProjectsCache _allProjectsCache) : base(state, multiColumnHeader) { allProjectsCache = _allProjectsCache; showBorder = true; rowHeight = CONST_ROW_HEIGHTS; showAlternatingRowBackgrounds = true; customFoldoutYOffset = (CONST_ROW_HEIGHTS - EditorGUIUtility.singleLineHeight) * 0.5f; UpdateContentList(); ActionNotificationCenter.DefaultCenter.AddObserver(_data => { UpdateContentList(); Reload(); }, nameof(EditingProjectCommand)); } private void UpdateContentList() { var tmp_ProjectCacheData = allProjectsCache.GetEditingProjectData(); if (tmp_ProjectCacheData) { contentModels = tmp_ProjectCacheData.DetailCacheData.Contents ?? new List(); //Fix Asset's path tmp_ProjectCacheData.DetailCacheData.ChangeAssetsPath(tmp_ProjectCacheData.Project.DisplayName); } else contentModels = new List(); } protected override TreeViewItem BuildRoot() { return new TreeViewItem {id = -1, depth = -1, displayName = "Root"}; } protected override IList BuildRows(TreeViewItem root) { var tmp_Rows = GetRows() ?? new List(); tmp_Rows.Clear(); foreach (var tmp_Content in contentModels) { var tmp_ProjectTreeViewItem = CreateTreeViewItem(tmp_Content); root.AddChild(tmp_ProjectTreeViewItem); tmp_Rows.Add(tmp_ProjectTreeViewItem); } return tmp_Rows; } protected override void RowGUI(RowGUIArgs _args) { var tmp_ContentTreeViewItem = _args.item as ContentTreeViewItem; for (int tmp_Idx = 0; tmp_Idx < _args.GetNumVisibleColumns(); ++tmp_Idx) { var tmp_CellRect = _args.GetCellRect(tmp_Idx); CenterRectUsingSingleLineHeight(ref tmp_CellRect); var tmp_ColumnIndex = _args.GetColumn(tmp_Idx); Object tmp_Asset = null; if (tmp_ContentTreeViewItem != null) { tmp_Asset = AssetDatabase.LoadAssetAtPath(tmp_ContentTreeViewItem.ContentModel.AssetPathInUnity, typeof(object)); } switch ((ColumnsType) tmp_ColumnIndex) { case ColumnsType.Icon1: if (tmp_Asset) { Texture tmp_Icon = EditorGUIUtility.ObjectContent(tmp_Asset, tmp_Asset.GetType()).image; Rect tmp_IconRect = new Rect(tmp_CellRect.x + 6, tmp_CellRect.y + 3, 12, 12); GUI.DrawTexture(tmp_IconRect, tmp_Icon, ScaleMode.ScaleToFit); } else if (tmp_ContentTreeViewItem != null) { Texture tmp_Icon = EditorGUIUtility.FindTexture("console.erroricon.sml"); Rect tmp_IconRect = new Rect(tmp_CellRect.x + 6, tmp_CellRect.y + 3, 12, 12); GUI.DrawTexture(tmp_IconRect, tmp_Icon, ScaleMode.ScaleToFit); } else { EditorGUI.LabelField(tmp_CellRect, new GUIContent(EditorGUIUtility.FindTexture("d_GameObject Icon"), "")); } break; case ColumnsType.Name: if (tmp_Asset == null) { EditorGUI.DrawRect(tmp_CellRect, new Color(1f, 0f, 0f)); } EditorGUI.LabelField(tmp_CellRect, tmp_Asset == null ? $"{_args.item.displayName}(Asset Missing)" : _args.item.displayName); break; case ColumnsType.AssetPath: if (tmp_ContentTreeViewItem != null) { EditorGUI.LabelField(tmp_CellRect, tmp_ContentTreeViewItem.ContentModel.AssetPathInUnity); } break; } } } ContentTreeViewItem CreateTreeViewItem(ContentModel _model) { return new ContentTreeViewItem {id = _model.Id, displayName = _model.DisplayName, ContentModel = _model}; } protected override void ContextClickedItem(int _id) { base.ContextClickedItem(_id); var tmp_Item = contentModels.FirstOrDefault(_element => _element.Id == _id); GenericMenu tmp_Menu = new GenericMenu(); tmp_Menu.AddItem(new GUIContent("Ping Object"), false, () => { var tmp_Object = AssetDatabase.LoadAssetAtPath(tmp_Item.AssetPathInUnity, typeof(Object)); EditorGUIUtility.PingObject(tmp_Object); }); tmp_Menu.AddItem(new GUIContent("Short Name"), false, () => { tmp_Item.DisplayName = Path.GetFileNameWithoutExtension(tmp_Item.DisplayName); RenameEnded(new RenameEndedArgs { newName = Path.GetFileNameWithoutExtension(tmp_Item.DisplayName), itemID = tmp_Item.Id, acceptedRename = true }); }); tmp_Menu.AddItem(new GUIContent("Rename"), false, () => { if (BeginRename(FindItem(_id, rootItem))) { Reload(); } }); tmp_Menu.AddSeparator(""); tmp_Menu.AddItem(new GUIContent("Remove"), false, () => { var tmp_ElementIDs = GetSelection(); var tmp_Elements = contentModels.Where(_element => tmp_ElementIDs.Contains(_element.Id)).ToArray(); foreach (var tmp_Element in tmp_Elements) contentModels.Remove(tmp_Element); removedElement?.Invoke(); ReupdateContentId(); Reload(); }); tmp_Menu.ShowAsContext(); } /// /// 重新更新列表ID /// private void ReupdateContentId() { // 重新更新ID int tmp_StartId = 0; foreach (ContentModel tmp_Content in contentModels) { tmp_Content.Id = tmp_StartId; tmp_StartId++; } } public override void OnGUI(Rect _rect) { base.OnGUI(_rect); } protected override bool CanStartDrag(CanStartDragArgs _args) { return base.CanStartDrag(_args); } protected override void SetupDragAndDrop(SetupDragAndDropArgs _args) { base.SetupDragAndDrop(_args); } protected override DragAndDropVisualMode HandleDragAndDrop(DragAndDropArgs _args) { if (!CanDragAndDrop) return DragAndDropVisualMode.None; if (!_args.performDrop) return DragAndDropVisualMode.Link; DragAndDrop.visualMode = DragAndDropVisualMode.Link; DragAndDrop.AcceptDrag(); bool tmp_AddedSuccessed = false; foreach (Object tmp_DraggedObject in DragAndDrop.objectReferences) { if (tmp_DraggedObject is DefaultAsset) { continue; } string tmp_AssetPath = AssetDatabase.GetAssetPath(tmp_DraggedObject); var tmp_Exists = contentModels.Exists(_c => _c.DisplayName == tmp_DraggedObject.name); if (tmp_Exists) { EditorUtility.DisplayDialog("Already In Package Error", $"The '{tmp_DraggedObject.name}' asset is already in Package. Do not add it repeatedly", "OK"); break; } // 传入进来的是Object,用GetType会把Sprite认为是Texture2D导致类型拿错。 string tmp_TypeName = tmp_DraggedObject.GetType().FullName; var tmp_Assets = AssetDatabase.LoadAllAssetsAtPath(tmp_AssetPath); foreach (var tmp_Asset in tmp_Assets) { if (tmp_Asset is not Sprite) continue; tmp_TypeName = typeof(Sprite).FullName; } var tmp_Content = new ContentModel { Id = GenerateContentIndex, DisplayName = tmp_DraggedObject.name, AssetPathInUnity = tmp_AssetPath, Type = tmp_TypeName }; contentModels.Add(tmp_Content); addedNewElement?.Invoke(); tmp_AddedSuccessed = true; } if (!tmp_AddedSuccessed) return DragAndDropVisualMode.Generic; contentModels.Sort((a, b) => { int tmp_TypeComparison = string.Compare(a.Type.Split(".").Last(), b.Type.Split(".").Last(), StringComparison.Ordinal); if (tmp_TypeComparison != 0) return tmp_TypeComparison; // Type不同,直接用Type的比较结果 // Type相同,再比较AssetPathInUnity return string.Compare(a.DisplayName, b.DisplayName, StringComparison.Ordinal); }); ReupdateContentId(); Reload(); return DragAndDropVisualMode.Generic; } protected override void RenameEnded(RenameEndedArgs _args) { base.RenameEnded(_args); if (!_args.acceptedRename) return; allProjectsCache.GetEditingProjectData().DetailCacheData.Find(_args.itemID).DisplayName = _args.newName; Reload(); } protected override bool CanRename(TreeViewItem _item) { return true; } /// /// Generate the content item index id。 /// Avoid drag-and-drop new object to contents view after the item is removed to generate the wrong id /// private int GenerateContentIndex => contentModels.Count == 0 ? 0 : contentModels[contentModels.Count - 1].Id + 1; } }