#if UNITY_EDITOR using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEngine.Events; using NeoFPS; using System; namespace NeoFPSEditor { public class ProjectHierarchyBrowser : EditorWindow { static void GetObject(UnityAction onPicked, UnityAction onCancelled, GameObjectFilter filter, string typeName) { ProjectHierarchyBrowser window = GetWindow(true, "Project Browser", true); window.minSize = new Vector2(320, 480); window.m_Countdown = 2; window.m_TypeName = typeName; window.m_Filter = filter; window.m_Selection = null; window.m_OnPicked = onPicked; window.m_OnCancelled = onCancelled; s_Instance = window; // Sub to editor update to initialise contents EditorApplication.update += window.InitialiseHierarchy; } public static void GetPrefab(UnityAction onPicked, UnityAction onCancelled) { GetPrefab(onPicked, onCancelled, null); } public static void GetPrefab(UnityAction onPicked, UnityAction onCancelled, GameObjectFilter filter) { GetObject( (obj) => { var prefab = obj as GameObject; if (onPicked != null) onPicked(prefab); }, onCancelled, filter, "GameObject"); s_Instance.m_BrowsingPrefabs = true; } public static void GetPrefabWithComponent(UnityAction onPicked, UnityAction onCancelled) where T : class { GetObject( (obj) => { var prefab = obj as GameObject; if (onPicked != null) onPicked(prefab.GetComponent()); }, onCancelled, FilterComponent, "GameObject"); s_Instance.m_BrowsingPrefabs = true; } public static void GetPrefabWithComponent(UnityAction onPicked, UnityAction onCancelled, ComponentFilter filter) where T : class { GetObject( (obj) => { var prefab = obj as GameObject; if (onPicked != null) { if (prefab != null) onPicked(prefab.GetComponent()); else onPicked(null); } }, onCancelled, (obj) => { var component = obj.GetComponent(); if (component == null) return false; if (filter != null && !filter(component)) return false; return true; }, "GameObject"); s_Instance.m_BrowsingPrefabs = true; } public static void GetAsset(UnityAction onPicked, Type t, UnityAction onCancelled) { GetObject( (obj) => { var asset = obj as ScriptableObject; if (onPicked != null) onPicked(asset); }, onCancelled, null, t.Name); s_Instance.m_BrowsingPrefabs = false; } public static void GetAsset(UnityAction onPicked, UnityAction onCancelled) where T : class { GetObject( (obj) => { var asset = obj as T; if (onPicked != null) onPicked(asset); }, onCancelled, null, typeof(T).Name); s_Instance.m_BrowsingPrefabs = false; } static bool FilterComponent(GameObject obj) { return obj.GetComponent() != null; } private static ProjectHierarchyBrowser s_Instance = null; private string m_TypeName = string.Empty; private GameObjectFilter m_Filter = null; private Vector2 m_ScrollPosition = Vector2.zero; private UnityEngine.Object m_Selection = null; private UnityAction m_OnPicked = null; private UnityAction m_OnCancelled = null; private bool m_DoubleClicked = false; private bool m_Picked = false; private bool m_BrowsingPrefabs = false; private int m_Countdown = 0; private string m_NameFilter = string.Empty; private List m_RootFolders = new List(); private List m_RootObjects = new List(); private List m_SortedObjects = new List(); private GUIStyle m_BackgroundStyleNormal = null; private GUIStyle m_BackgroundStyleSelected = null; private GUIStyle m_FoldoutStyleExpanded = null; private GUIStyle m_FoldoutStyleContracted = null; private GUIStyle m_NameStyleNormal = null; private GUIStyle m_NameStyleSelected = null; private GUIContent m_FolderLabelContent = null; private GUIContent m_PrefabLabelContent = null; private GUIContent m_AssetLabelContent = null; private GUIContent objectLabelContent { get { if (m_BrowsingPrefabs) return m_PrefabLabelContent; else return m_AssetLabelContent; } } private void Awake() { // Get background styles m_BackgroundStyleNormal = new GUIStyle(); m_BackgroundStyleNormal.fixedHeight = 20; m_BackgroundStyleSelected = new GUIStyle(m_BackgroundStyleNormal); m_BackgroundStyleSelected.normal.background = new GUIStyle("OL SelectedRow").normal.background; // Get foldout styles m_FoldoutStyleExpanded = new GUIStyle(); m_FoldoutStyleExpanded.fixedHeight = 12; m_FoldoutStyleExpanded.fixedWidth = 12; m_FoldoutStyleExpanded.normal.background = EditorGUIUtility.FindTexture("d_Toolbar Minus"); m_FoldoutStyleExpanded.margin = new RectOffset(0, 0, 4, 0); //m_FoldoutStyleExpanded.overflow = new RectOffset(0, 0, 0, 4); m_FoldoutStyleContracted = new GUIStyle(m_FoldoutStyleExpanded); m_FoldoutStyleContracted.normal.background = EditorGUIUtility.FindTexture("d_Toolbar Plus"); // Get label styles m_NameStyleNormal = new GUIStyle(EditorStyles.label); m_NameStyleNormal.margin.left -= 4; m_NameStyleSelected = new GUIStyle(m_NameStyleNormal); m_NameStyleSelected.normal.textColor = Color.white; m_FolderLabelContent = EditorGUIUtility.IconContent("Folder Icon"); m_PrefabLabelContent = EditorGUIUtility.IconContent("d_Prefab Icon"); m_AssetLabelContent = EditorGUIUtility.IconContent("ScriptableObject Icon"); m_NameFilter = string.Empty; } abstract class HierarchyNode { public abstract void Draw(int indent); } class FolderNode : HierarchyNode { private string m_Name = string.Empty; private bool m_Expanded = true; private List m_SubFolders = new List(); private List m_Objects = new List(); public string name { get { return m_Name; } } public FolderNode(string name) { m_Name = name; m_Expanded = true; } public override void Draw(int indent) { using (new EditorGUILayout.HorizontalScope(GUILayout.Width(s_Instance.position.width - 32))) { // Indent if (indent > 0) GUILayout.Space(12 * indent); // Foldout button if (GUILayout.Button(GUIContent.none, m_Expanded ? s_Instance.m_FoldoutStyleExpanded : s_Instance.m_FoldoutStyleContracted)) m_Expanded = !m_Expanded; var content = new GUIContent(s_Instance.m_FolderLabelContent); content.text = m_Name; if (GUILayout.Button(content, s_Instance.m_NameStyleNormal, GUILayout.Height(20))) m_Expanded = !m_Expanded; } if (m_Expanded) { for (int i = 0; i < m_SubFolders.Count; ++i) m_SubFolders[i].Draw(indent + 1); for (int i = 0; i < m_Objects.Count; ++i) m_Objects[i].Draw(indent + 1); } } public FolderNode AddSubFolder(string subFolder) { for (int i = 0; i < m_SubFolders.Count; ++i) { if (m_SubFolders[i].m_Name == subFolder) return m_SubFolders[i]; } var result = new FolderNode(subFolder); m_SubFolders.Add(result); return result; } public ObjectNode AddObject(UnityEngine.Object obj) { for (int i = 0; i < m_Objects.Count; ++i) { if (m_Objects[i].name == obj.name) return m_Objects[i]; } var result = new ObjectNode(obj); m_Objects.Add(result); return result; } public FolderNode GetSubFolder(string subFolder) { for (int i = 0; i < m_SubFolders.Count; ++i) { if (m_SubFolders[i].m_Name == subFolder) return m_SubFolders[i]; } return null; } public ObjectNode GetObject(string objectName) { for (int i = 0; i < m_Objects.Count; ++i) { if (m_Objects[i].name == objectName) return m_Objects[i]; } return null; } } class ObjectNode : HierarchyNode { private UnityEngine.Object m_Object = null; public string name { get { return m_Object.name; } } public ObjectNode(UnityEngine.Object obj) { m_Object = obj; } public override void Draw(int indent) { bool isSelected = s_Instance.m_Selection == m_Object; var backgroundStyle = isSelected ? s_Instance.m_BackgroundStyleSelected : s_Instance.m_BackgroundStyleNormal; var labelStyle = isSelected ? s_Instance.m_NameStyleSelected : s_Instance.m_NameStyleNormal; using (new EditorGUILayout.HorizontalScope(backgroundStyle, GUILayout.Width(s_Instance.position.width - 32))) { // Indent if (indent > 0) GUILayout.Space(12 * indent + 12); var content = new GUIContent(s_Instance.objectLabelContent); content.text = m_Object.name; if (GUILayout.Button(content, labelStyle, GUILayout.Height(20))) { s_Instance.m_Selection = m_Object; if (s_Instance.m_DoubleClicked) { s_Instance.m_Picked = true; s_Instance.Close(); throw new ExitGUIException(); // Abort layout / GUI } } } } } void InitialiseHierarchy() { if (--m_Countdown == 0) { // Gather and filter objects var guids = AssetDatabase.FindAssets("t:" + m_TypeName); foreach (var guid in guids) { var path = AssetDatabase.GUIDToAssetPath(guid); var obj = AssetDatabase.LoadMainAssetAtPath(path); if (m_Filter == null || m_Filter(obj as GameObject)) AddObject(obj, path); } // Sort objects m_SortedObjects.Sort((lhs, rhs) => { return string.Compare(lhs.name, rhs.name); }); // Complete and unsubscribe EditorApplication.update -= InitialiseHierarchy; } } static readonly char[] k_PathSeparators = { '/', '\\' }; void AddObject(UnityEngine.Object obj, string path) { ObjectNode node = null; string[] folders = path.Split(k_PathSeparators); if (folders.Length <= 2) { node = new ObjectNode(obj); m_RootObjects.Add(node); } else { FolderNode itr = null; // Get root folder for (int i = 0; i < m_RootFolders.Count; ++i) { if (m_RootFolders[i].name == folders[1]) { itr = m_RootFolders[i]; break; } } // Not found, so add if (itr == null) { itr = new FolderNode(folders[1]); m_RootFolders.Add(itr); } // Add sub-folders (if required) for (int i = 2; i < folders.Length - 1; ++i) itr = itr.AddSubFolder(folders[i]); // Add object node = itr.AddObject(obj); } // Add to sorted objects list m_SortedObjects.Add(node); } private void OnGUI() { if (Event.current.type == EventType.MouseDown) { m_DoubleClicked = (Event.current.clickCount > 1); } using (new EditorGUILayout.VerticalScope()) { // Draw the filter GUILayout.Space(4); m_NameFilter = EditorGUILayout.TextField("Filter", m_NameFilter); // Draw the hierarchy list var gs = new GUIStyle(); gs.stretchWidth = false; using (var scroller = new EditorGUILayout.ScrollViewScope(m_ScrollPosition, false, true, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUI.skin.box)) { m_ScrollPosition = scroller.scrollPosition; using (new EditorGUILayout.VerticalScope()) { if (m_Countdown > 0) GUILayout.Label("Searching..."); else { if (!string.IsNullOrWhiteSpace(m_NameFilter)) { string lowerCaseNameFilter = m_NameFilter.ToLower(); for (int i = 0; i < m_SortedObjects.Count; ++i) { if (m_SortedObjects[i].name.ToLower().Contains(lowerCaseNameFilter)) m_SortedObjects[i].Draw(0); } } else { for (int i = 0; i < m_RootFolders.Count; ++i) m_RootFolders[i].Draw(0); for (int i = 0; i < m_RootObjects.Count; ++i) m_RootObjects[i].Draw(0); } } } } // Show select object button (only valid if there is a selection) if (m_Selection == null) { GUI.enabled = false; GUILayout.Button("Pick: "); GUI.enabled = true; } else { if (GUILayout.Button(string.Format("Pick: \"{0}\"", m_Selection.name))) { m_Picked = true; Close(); } } // Show select none button if (GUILayout.Button("Pick None")) { m_Picked = true; m_Selection = null; Close(); } } } private void OnLostFocus() { Close(); } private void OnDestroy() { if (m_Picked) { if (m_OnPicked != null) m_OnPicked(m_Selection); m_Selection = null; } else { if (m_OnCancelled != null) m_OnCancelled(); } m_RootFolders.Clear(); m_RootObjects.Clear(); m_SortedObjects.Clear(); s_Instance = null; } public static bool FilterByComponent(GameObject obj) where T : Component { return (obj.GetComponent() != null); } } } #endif