Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Feedback Folder description

Discussion in 'Editor & General Support' started by darklight98, Nov 2, 2019.

  1. darklight98

    darklight98

    Joined:
    Jan 24, 2013
    Posts:
    18
    I know this is such a small feature that would be added. But I think it makes a huge difference in big projects or third party ones. Instead of having text files spread across your project Assets, blending them into folder could show useful if the contents of the folder end up being more complex to explain than a title length string (the folder name, that has its limitation).
     
  2. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    This is totally doable using an editor script! This was a fun little problem to solve:

    Code (CSharp):
    1. using System.IO;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomEditor( typeof( DefaultAsset ), isFallback = true )]
    6. public class FolderDescription : Editor {
    7.    static class Styles {
    8.        public static readonly GUIStyle invisibleTextEditor = new GUIStyle( "TextArea" );
    9.  
    10.        static Styles () {
    11.            var label = EditorStyles.label;
    12.            invisibleTextEditor.normal = new GUIStyleState();
    13.            invisibleTextEditor.normal.background = label.normal.background;
    14.            invisibleTextEditor.normal.textColor = label.normal.textColor;
    15.        }
    16.    }
    17.  
    18.    const string descriptionFilename = ".desc";
    19.  
    20.    bool isFolder;
    21.    string description;
    22.  
    23.    private void OnEnable () {
    24.        var path = AssetDatabase.GetAssetPath( target );
    25.  
    26.        if( Directory.Exists( path ) ) {
    27.            isFolder = true;
    28.            var descriptionPath = Path.Combine( path, descriptionFilename );
    29.  
    30.            try {
    31.                description = File.ReadAllText( descriptionPath );
    32.            } catch( IOException ) {}
    33.        }
    34.    }
    35.  
    36.    public override void OnInspectorGUI () {
    37.        if( isFolder ) {
    38.            GUI.enabled = true;
    39.            EditorGUI.BeginChangeCheck();
    40.  
    41.            var descriptionContent = new GUIContent( description );
    42.  
    43.            description = EditorGUILayout.TextArea( description, Styles.invisibleTextEditor, GUILayout.ExpandHeight( false ), GUILayout.ExpandWidth( true ) );
    44.  
    45.            if( string.IsNullOrEmpty( description ) && GUIUtility.keyboardControl == 0 ) {
    46.                var rect = GUILayoutUtility.GetLastRect();
    47.  
    48.                GUI.Label( rect, "Click to add a folder description", EditorStyles.centeredGreyMiniLabel );
    49.            }
    50.  
    51.            if( EditorGUI.EndChangeCheck() ) {
    52.                // we get the asset path again instead of caching it in case the folder has moved since OnEnable was called
    53.                var descriptionPath = Path.Combine( AssetDatabase.GetAssetPath( target ), descriptionFilename );
    54.  
    55.                if( !string.IsNullOrEmpty( description ) ) {
    56.                    try {
    57.                        File.SetAttributes( descriptionPath, FileAttributes.Normal );
    58.                    } catch( IOException ) {}
    59.  
    60.                    File.WriteAllText( descriptionPath, description );
    61.  
    62.                    try {
    63.                        File.SetAttributes( descriptionPath, FileAttributes.Hidden );
    64.                    } catch( IOException ) {}
    65.                } else {
    66.                    try {
    67.                        File.Delete( descriptionPath );
    68.                    } catch( IOException ) {}
    69.                }
    70.            }
    71.        } else {
    72.            DrawDefaultInspector();
    73.        }
    74.    }
    75. }
    It works by creating a hidden file inside the given folder that contains a description for that folder. The file is also hidden from Unity so it isn't imported. If you want visible description files, just strip out the code that sets the file attributes to be hidden. If you want the files to be visible in Unity for whatever reason, rename them to something that doesn't start with a dot.
     
    Last edited: Nov 2, 2019
    darklight98 likes this.
  3. darklight98

    darklight98

    Joined:
    Jan 24, 2013
    Posts:
    18
    This is actually incredible, I didn't know you could change folders. But sadly it's not working for me, and I can't find any errors in the code. My unity version is 2019.2.9f1 Personal
     
  4. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    The script was built for 2018. It seems that there are some incompatibilities between these two versions.

    EDIT: Here's a version that supports 2019. For whatever reason OnInspectorGUI is not called for folders in 2019, so the script now draws the GUI in OnHeaderGUI instead for 2019+:

    Code (CSharp):
    1. using System.IO;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomEditor( typeof( DefaultAsset ), isFallback = true )]
    6. public class FolderDescription : Editor {
    7.     static class Styles {
    8.         public static readonly GUIStyle invisibleTextEditor = new GUIStyle( "TextArea" );
    9.  
    10.         static Styles () {
    11.             var label = EditorStyles.label;
    12.             invisibleTextEditor.normal = new GUIStyleState();
    13.             invisibleTextEditor.normal.background = label.normal.background;
    14.             invisibleTextEditor.normal.textColor = label.normal.textColor;
    15.         }
    16.     }
    17.  
    18.     const string descriptionFilename = ".desc";
    19.  
    20.     bool isFolder;
    21.     string description;
    22.  
    23.     private void OnEnable () {
    24.         var path = AssetDatabase.GetAssetPath( target );
    25.  
    26.         if( Directory.Exists( path ) ) {
    27.             isFolder = true;
    28.             var descriptionPath = Path.Combine( path, descriptionFilename );
    29.  
    30.             try {
    31.                 description = File.ReadAllText( descriptionPath );
    32.             } catch( IOException ) {}
    33.         }
    34.     }
    35.  
    36. #if UNITY_2019_1_OR_NEWER
    37.     protected override void OnHeaderGUI () {
    38.         base.OnHeaderGUI();
    39.  
    40.         if( isFolder ) {
    41.             DoDescriptionEditor();
    42.         }
    43.     }
    44. #else
    45.     public override void OnInspectorGUI () {
    46.         if( isFolder ) {
    47.             DoDescriptionEditor();
    48.         } else {
    49.             DrawDefaultInspector();
    50.         }
    51.     }
    52. #endif
    53.  
    54.     private void DoDescriptionEditor () {
    55.         GUI.enabled = true;
    56.         EditorGUI.BeginChangeCheck();
    57.  
    58.         var descriptionContent = new GUIContent( description );
    59.  
    60.         description = EditorGUILayout.TextArea( description, Styles.invisibleTextEditor, GUILayout.ExpandHeight( false ), GUILayout.ExpandWidth( true ) );
    61.  
    62.         if( string.IsNullOrEmpty( description ) && GUIUtility.keyboardControl == 0 ) {
    63.             var rect = GUILayoutUtility.GetLastRect();
    64.  
    65.             GUI.Label( rect, "Click to add a folder description", EditorStyles.centeredGreyMiniLabel );
    66.         }
    67.  
    68.         if( EditorGUI.EndChangeCheck() ) {
    69.             // we get the asset path again instead of caching it in case the folder has moved since OnEnable was called
    70.             var descriptionPath = Path.Combine( AssetDatabase.GetAssetPath( target ), descriptionFilename );
    71.  
    72.             if( !string.IsNullOrEmpty( description ) ) {
    73.                 try {
    74.                     File.SetAttributes( descriptionPath, FileAttributes.Normal );
    75.                 } catch( IOException ) { }
    76.  
    77.                 File.WriteAllText( descriptionPath, description );
    78.  
    79.                 try {
    80.                     File.SetAttributes( descriptionPath, FileAttributes.Hidden );
    81.                 } catch( IOException ) { }
    82.             } else {
    83.                 try {
    84.                     File.Delete( descriptionPath );
    85.                 } catch( IOException ) { }
    86.             }
    87.         }
    88.     }
    89. }
     
    Last edited: Nov 3, 2019
    darklight98 and Vectorbox like this.
  5. darklight98

    darklight98

    Joined:
    Jan 24, 2013
    Posts:
    18
    @Madgvox, I can't take you enough, my team has been able to organize so much better with such addition! This should be native.
     
  6. CausticLasagne

    CausticLasagne

    Joined:
    Oct 2, 2015
    Posts:
    26
    Here is an up-to-date version of the above script, working for 2020.1.1.
    - Added title label
    - Removed styling, to default GUI style
    - Added debug log for catch exceptions
    - Draws an additional 16 units in case you have Addressable Assets installed. The checkbox covers the description text area otherwise.

    Unity Please make this a stock feature!

    Code (CSharp):
    1. using System.IO;
    2. using UnityEditor;
    3. using UnityEngine;
    4.  
    5. [CustomEditor(typeof(DefaultAsset), isFallback = true)]
    6. public class FolderDescription : Editor
    7. {
    8.     // Thanks for all the fish!
    9.     const string descriptionFilename = ".desc";
    10.  
    11.     bool isFolder;
    12.     string description;
    13.  
    14.     private void OnEnable()
    15.     {
    16.         var path = AssetDatabase.GetAssetPath(target);
    17.  
    18.         if (Directory.Exists(path))
    19.         {
    20.             isFolder = true;
    21.             var descriptionPath = Path.Combine(path, descriptionFilename);
    22.  
    23.             try
    24.             {
    25.                 description = File.ReadAllText(descriptionPath);
    26.             }
    27.             catch (System.Exception ea) { Error(ea); } // Print to debug error message only if there was an advanced error
    28.         }
    29.     }
    30.  
    31. #if UNITY_2019_1_OR_NEWER
    32.     protected override void OnHeaderGUI()
    33.     {
    34.         base.OnHeaderGUI();
    35.  
    36.         if (isFolder)
    37.         {
    38.             DoDescriptionEditor();
    39.         }
    40.     }
    41. #endif
    42.  
    43.     private void DoDescriptionEditor()
    44.     {
    45.         GUI.enabled = true;
    46.         EditorGUI.BeginChangeCheck();
    47.  
    48.         var descriptionContent = new GUIContent(description);
    49.  
    50.         GUILayout.Label("Description");
    51.         EditorGUILayout.Space(8);
    52.  
    53.         description = EditorGUILayout.TextArea(description, GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true)); // Reverted styling to default
    54.  
    55.         EditorGUILayout.Space(16);
    56.  
    57.         if (EditorGUI.EndChangeCheck())
    58.         {
    59.             // we get the asset path again instead of caching it in case the folder has moved since OnEnable was called
    60.             var descriptionPath = Path.Combine(AssetDatabase.GetAssetPath(target), descriptionFilename);
    61.  
    62.             if (!string.IsNullOrEmpty(description))
    63.             {
    64.                 try
    65.                 {
    66.                     File.SetAttributes(descriptionPath, FileAttributes.Normal);
    67.                 }
    68.                 catch (System.Exception ea) { Error(ea); } // Print to debug error message only if there was an advanced error
    69.  
    70.                 File.WriteAllText(descriptionPath, description);
    71.  
    72.                 try
    73.                 {
    74.                     File.SetAttributes(descriptionPath, FileAttributes.Normal);
    75.                 }
    76.                 catch (System.Exception ea) { Error(ea); } // Print to debug error message only if there was an advanced error
    77.             }
    78.             else
    79.             {
    80.                 try
    81.                 {
    82.                     File.Delete(descriptionPath);
    83.                 }
    84.                 catch (System.Exception ea) { Error(ea); } // Print to debug error message only if there was an advanced error
    85.             }
    86.         }
    87.     }
    88.  
    89.     void Error(System.Exception ea)
    90.     {
    91.         if (ea is System.IO.FileNotFoundException)
    92.         {
    93.             return;
    94.         }
    95.         Debug.LogError(ea.ToString());
    96.     }
    97. }
     
    Last edited: Apr 14, 2021
    ms502040 and thenme like this.