Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice
  2. We've opened up a space to discuss, share feedback, and showcase everything related to the Unity Shader Graph! Come show us what you've made.
    Dismiss Notice

Tutorial: How to to show specific folder content in the project window via editor scripting

Discussion in 'Extensions & OnGUI' started by Xarbrough, Dec 10, 2017.

  1. Xarbrough


    Dec 11, 2014
    I wanted to find a way to show a specific folder and display its content in the Unity project panel from custom editor code. Too bad, there is no official API available for this. This is why I went down the rabbit hole to find out how Unity does it internally and used reflection to achieve what I wanted.

    To make it a little more clear: I basically want to use a similar behavior like

    Code (CSharp):
    1. EditorGUIUtility.PingObject(instanceID);
    but to show the contents of a specific folder asset, whose instance id I already know, instead.

    A simple way of doing this would be to get the folder path and then search the content for an arbitrary asset and then ping it. However, this has two drawbacks: First, it requires the folder to actually have content. You can't open empty folders this way. Secondly, it is not easy to reproduce the project window's sorting and it's generally a little ugly to ping and select an arbitrary asset from a folder when I actually just want to open it like any other browser. This is why I decided to search for the internal functionality of actually displaying the folder contents, which I found in the internal method ShowFolderContents of the ProjectBrowser class.

    I hope this helps someone:

    [Tested with Unity 2017.2.0f3]

    Code (CSharp):
    1. /// <summary>
    2. /// Selects a folder in the project window and shows its content.
    3. /// Opens a new project window, if none is open yet.
    4. /// </summary>
    5. /// <param name="folderInstanceID">The instance of the folder asset to open.</param>
    6. private static void ShowFolderContents(int folderInstanceID)
    7. {
    8.     // Find the internal ProjectBrowser class in the editor assembly.
    9.     Assembly editorAssembly = typeof(Editor).Assembly;
    10.     System.Type projectBrowserType = editorAssembly.GetType("UnityEditor.ProjectBrowser");
    12.     // This is the internal method, which performs the desired action.
    13.     // Should only be called if the project window is in two column mode.
    14.     MethodInfo showFolderContents = projectBrowserType.GetMethod(
    15.         "ShowFolderContents", BindingFlags.Instance | BindingFlags.NonPublic);
    17.     // Find any open project browser windows.
    18.     Object[] projectBrowserInstances = Resources.FindObjectsOfTypeAll(projectBrowserType);
    20.     if (projectBrowserInstances.Length > 0)
    21.     {
    22.         for (int i = 0; i < projectBrowserInstances.Length; i++)
    23.             ShowFolderContentsInternal(projectBrowserInstances[i], showFolderContents, folderInstanceID);
    24.     }
    25.     else
    26.     {
    27.         EditorWindow projectBrowser = OpenNewProjectBrowser(projectBrowserType);
    28.         ShowFolderContentsInternal(projectBrowser, showFolderContents, folderInstanceID);
    29.     }
    30. }
    32. private static void ShowFolderContentsInternal(Object projectBrowser, MethodInfo showFolderContents, int folderInstanceID)
    33. {
    34.     // Sadly, there is no method to check for the view mode.
    35.     // We can use the serialized object to find the private property.
    36.     SerializedObject serializedObject = new SerializedObject(projectBrowser);
    37.     bool inTwoColumnMode = serializedObject.FindProperty("m_ViewMode").enumValueIndex == 1;
    39.     if (!inTwoColumnMode)
    40.     {
    41.         // If the browser is not in two column mode, we must set it to show the folder contents.
    42.         MethodInfo setTwoColumns = projectBrowser.GetType().GetMethod(
    43.             "SetTwoColumns", BindingFlags.Instance | BindingFlags.NonPublic);
    44.         setTwoColumns.Invoke(projectBrowser, null);
    45.     }
    47.     bool revealAndFrameInFolderTree = true;
    48.     showFolderContents.Invoke(projectBrowser, new object[] { folderInstanceID, revealAndFrameInFolderTree });
    49. }
    51. private static EditorWindow OpenNewProjectBrowser(System.Type projectBrowserType)
    52. {
    53.     EditorWindow projectBrowser = EditorWindow.GetWindow(projectBrowserType);
    54.     projectBrowser.Show();
    56.     // Unity does some special initialization logic, which we must call,
    57.     // before we can use the ShowFolderContents method (else we get a NullReferenceException).
    58.     MethodInfo init = projectBrowserType.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public);
    59.     init.Invoke(projectBrowser, null);
    61.     return projectBrowser;
    62. }
    As you can see, the code used a lot of reflection and can (should) be optimized in production code. Instead of performing all the type and method searching in every call, we can do it once at initialization and store delegates of the reflected methods. Also, I have omitted null checks. You probably want to handle failed reflection attempts.

    Disclaimer: My code assumes internal, undocumented and unsupported functionality, which might change at any point. It might not work in other Unity versions. However, I personally am not afraid of using internal methods, because they still tend to be fairly stable over many years, and I can usually update my own code relatively easily.

    Feature request: I'd like to see an official API to show folder content in the project browser. :)
    Also see:
    Last edited: Dec 15, 2017
  2. shawn


    Unity Technologies

    Aug 4, 2007
    Good stuff! Thanks for sharing your discoveries with the community. :)
  3. halley


    Aug 26, 2013
    Sorry for necropost. I'd love to be able to use this, but I can't figure out how to convert a
    string path
    into an
    int folderInstanceID
    . For example, a
    overload would be nice. I may be missing something but the AssetDatabase isn't too helpful in this regard.
  4. Madgvox


    Apr 13, 2014
    Code (CSharp):
    1. AssetDatabase.LoadAssetAtPath<Object>( path ).GetInstanceID();
    halley likes this.
  5. halley


    Aug 26, 2013
    Thanks a lot! I got that to work with a little tweak; I had to specify <UnityEngine.Object> specifically.