Search Unity

  1. Check out the Unite LA keynote for updates on the Visual Effect Editor, the FPS Sample, ECS, Unity for Film and more! Watch it now!
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  5. Want to see the most recent patch releases? Take a peek at the patch release page.
    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

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    289
    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");
    11.  
    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);
    16.  
    17.     // Find any open project browser windows.
    18.     Object[] projectBrowserInstances = Resources.FindObjectsOfTypeAll(projectBrowserType);
    19.  
    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. }
    31.  
    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;
    38.  
    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.     }
    46.  
    47.     bool revealAndFrameInFolderTree = true;
    48.     showFolderContents.Invoke(projectBrowser, new object[] { folderInstanceID, revealAndFrameInFolderTree });
    49. }
    50.  
    51. private static EditorWindow OpenNewProjectBrowser(System.Type projectBrowserType)
    52. {
    53.     EditorWindow projectBrowser = EditorWindow.GetWindow(projectBrowserType);
    54.     projectBrowser.Show();
    55.  
    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);
    60.  
    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: https://feedback.unity3d.com/sugges...er-in-the-project-window-via-editor-scripting
     
    Last edited: Dec 15, 2017
  2. shawn

    shawn

    Unity Technologies

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

    halley

    Joined:
    Aug 26, 2013
    Posts:
    450
    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
    ShowFolderContents("Assets")
    overload would be nice. I may be missing something but the AssetDatabase isn't too helpful in this regard.
     
  4. Madgvox

    Madgvox

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

    halley

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