Search Unity

HelpAttribute - allows you to use HelpBox in the Unity inspector window

Discussion in 'Immediate Mode GUI (IMGUI)' started by reblGreen, Mar 24, 2017.

  1. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    Hey guys, my first post I think.

    Not sure if this has been done before or if it's built in to Unity (if not, it should be), did some searching and nothing turned up...

    I rolled a simple PropertyDrawer and PropertyAttribute script which allows you to use the HelpBox you see in various places inside the Unity IDE, to decorate your inspector properties. Using a [HelpAttribute] you can set the text content and icon image to show help text \and usage information or instructions above any property in the inspector.

    I thought it might be useful to the community for use in Asset Store assets or personal projects. Unity has an awesome community and hopefully you'll accept my first addition.

    The script is open source at https://github.com/johnearnshaw/unity-inspector-help/ and MIT license so commercial use allowed.

    Here's a few screenshots:

    These two screenshots show the HelpBox as it is used internally in Unity IDE.




    Here is a screenshot showing [HelpAttribute] usage on inspector visible properties from within a UnityBehavior script.



    See the Readme in the Github repo for full details on usage and restrictions:
    https://github.com/johnearnshaw/unity-inspector-help/

    Not sure if this is posted in the right place so move it, reference it or do what ever you want with it. I just hope someone finds this script useful :)
     
  2. ShawnFeatherly

    ShawnFeatherly

    Joined:
    Feb 22, 2013
    Posts:
    57
    Nice! What do you think of using a scrollbar in a note instead of trying to resize the notes height? I was messing with your code trying to get the help notes to resize better. Would likely take a bunch of code to get them to resize correctly which ruins the simplicity of your code.
     
    emilmsp likes this.
  3. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    Yeah, I know what you're saying but I think using a scrollbar would require a CustomEditor rather than using the unity built in EditorGUI.HelpBox. The resizing was a pain and in the end I just settled for the pre-calculated constants rather than trying to get an accurate resizing function. I just try and keep to text to a minimum and to the point, but if it gets big I throw a couple of newlines at the end of the text to expand it.

    I think the more elegant way to go would be calculating the size with GUIContent but couldn't figure it out, or should I say didn't have time to spend on it. Something along the lines of:

    Code (csharp):
    1. GUIStyle style = GUIStyle.none;
    2. style.CalcSize(new GUIContent(helpAttribute.help));
    I think you would need to get access to the GUIStyle used by the inspector to know the font size etc... Maybe someone from Unity GUI dev team can shed some light?

    There's this:

    Code (csharp):
    1. EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).GetStyle("");
    Which takes a string to get the GUIStyle but I haven't tried it. Maybe when I get more time...

    It could be as simple as "HelpBox"?
     
    Last edited: Mar 27, 2017
  4. reblGreen

    reblGreen

    Joined:
    Jan 29, 2015
    Posts:
    7
    @ShawnFeatherly, you got me hooked on fixing this one ;). I've updated the HelpAttribute.cs script in the repo to do a better job of computing the height at runtime.

    I also commented up the code to help peeps (who may require it) to understand what's going on a little better. I still had to leave some pre-calculated stuff in there for the custom MultilineAttribute handling and the minimum height and margin etc... But it's definitely a big improvement.
     
    ShawnFeatherly likes this.
  5. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    ShawnFeatherly likes this.
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    FWIW, this is the DecoratorDrawer that I use, in case it's helpful to anyone:

    HelpBoxAttribute.cs
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public enum HelpBoxMessageType { None, Info, Warning, Error }
    4.  
    5. public class HelpBoxAttribute : PropertyAttribute {
    6.  
    7.     public string text;
    8.     public HelpBoxMessageType messageType;
    9.  
    10.     public HelpBoxAttribute(string text, HelpBoxMessageType messageType = HelpBoxMessageType.None) {
    11.         this.text = text;
    12.         this.messageType = messageType;
    13.     }
    14. }
    Editor/HelpBoxAttributeDrawer.cs
    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [CustomPropertyDrawer(typeof(HelpBoxAttribute))]
    5. public class HelpBoxAttributeDrawer : DecoratorDrawer {
    6.  
    7.     public override float GetHeight() {
    8.         var helpBoxAttribute = attribute as HelpBoxAttribute;
    9.         if (helpBoxAttribute == null) return base.GetHeight();
    10.         var helpBoxStyle = (GUI.skin != null) ? GUI.skin.GetStyle("helpbox") : null;
    11.         if (helpBoxStyle == null) return base.GetHeight();
    12.         return Mathf.Max(40f, helpBoxStyle.CalcHeight(new GUIContent(helpBoxAttribute.text), EditorGUIUtility.currentViewWidth) + 4);
    13.     }
    14.  
    15.     public override void OnGUI(Rect position) {
    16.         var helpBoxAttribute = attribute as HelpBoxAttribute;
    17.         if (helpBoxAttribute == null) return;
    18.         EditorGUI.HelpBox(position, helpBoxAttribute.text, GetMessageType(helpBoxAttribute.messageType));
    19.     }
    20.  
    21.     private MessageType GetMessageType(HelpBoxMessageType helpBoxMessageType) {
    22.         switch (helpBoxMessageType) {
    23.             default:
    24.             case HelpBoxMessageType.None: return MessageType.None;
    25.             case HelpBoxMessageType.Info: return MessageType.Info;
    26.            case HelpBoxMessageType.Warning: return MessageType.Warning;
    27.             case HelpBoxMessageType.Error: return MessageType.Error;
    28.         }
    29.     }
    30. }
    Example:
    Code (csharp):
    1. [HelpBox("This is some help text for Data.", HelpBoxMessageType.Info)]
    2. public string data;
     
  7. LarryIRL

    LarryIRL

    Joined:
    Mar 31, 2014
    Posts:
    15
    Hi all, this post is very cool & thank you to the OP for taking the time to make it.

    Here is a somewhat simple hack I use. If anyone is interested.
    It's also handy want to display read only variables.

    Code (CSharp):
    1.  
    2. [ExecuteInEditMode]
    3. public class HelpBoxInDefaultInspector : MonoBehaviour {
    4.    
    5.     [SerializeField] [Multiline]
    6.     private string exampleHelpbox;
    7.  
    8.     [SerializeField] [Multiline]
    9.     private static string _exampleHelpbox = @"Some helpful info:
    10.    1) Some more example text.
    11.    2) Even more example text";
    12.  
    13.     private void Update() {
    14.         if (exampleHelpbox != _exampleHelpbox )
    15.             exampleHelpbox = _exampleHelpbox ;
    16.     }
    17. }
    18.  
    A property drawer / attribute is a much cleaner approach but I thought I'd share anyway.
     
    ExtraCat, Ishizuke and Rispat-Momit like this.
  8. ekergraphics

    ekergraphics

    Joined:
    Feb 22, 2017
    Posts:
    257
    This seems nice, but when I tried this I got the following error:

    I tried with both HelpBox and HelpBoxAttribute but it gave me the same error. I bet I'm missing something obvious.
     
    KarlKarl2000 likes this.
  9. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Did you put HelpBoxAttribute.cs in a non-Editor folder and HelpBoxAttributeDrawer.cs in an Editor folder?

    Can you post how you're using it in MaterialReplacer.cs?
     
    Roman_Keivan likes this.
  10. ekergraphics

    ekergraphics

    Joined:
    Feb 22, 2017
    Posts:
    257
    After I did that, the error was replaced with this instead:

    The line is:

    Code (CSharp):
    1.     [HelpBox("NOTE! If your new material is on other objects already, you will NOT be able to change back without also affecting those other objects.", HelpBoxMessageType.Info)]
     
    Roman_Keivan likes this.
  11. BinaryCats

    BinaryCats

    Joined:
    Feb 8, 2016
    Posts:
    317
    you're trying to put the attribute on a class, you can only do that if the attribute class is tagged with
    [AttributeUsage(AttributeTargets.Class)]

    however, decoration drawers do not draw for classes (?).

    you have to put the attribute on a field within the class

    I..e

    Code (csharp):
    1.  
    2. class myClass :monobehavior
    3. {
    4. [helpbox(words)]
    5. public int a = 0;
    6. }
     
    Roman_Keivan likes this.
  12. KarlKarl2000

    KarlKarl2000

    Joined:
    Jan 25, 2016
    Posts:
    606
    @usergame I can't seem to get this to work :(

    Is this correct? I'm on UNity 2018.1

    Code (CSharp):
    1.  [SerializeField]
    2.     [Help("check in here for controller bool")]
    Thanks for this tool!

     
    Roman_Keivan likes this.
  13. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    If you're using John Earnshaw's script, use [Help("xxx")]. If you're using my script, use [HelpBox("xxx", HelpBoxMessageType.yyy)]:
    Code (csharp):
    1. HelpBox("This is some help text for Data.", HelpBoxMessageType.Info)]
    2. public string data;
    and make sure to put the Drawer script in an Editor folder.
     
  14. seansteezy

    seansteezy

    Joined:
    Nov 28, 2013
    Posts:
    122
    Thanks for the great scripts, perfect timing too! Do you know how I would go about hiding the variable in the inspector, or is it even possible? Everything I do hides the message if the variable isn't visible (setting static, using [HideInInspector])...
     
    Roman_Keivan likes this.
  15. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    It depends on a visible variable. If you need a help box without any variables visible in the inspector, I think you'll need to write a custom inspector instead.
     
    Roman_Keivan likes this.
  16. DrMaxP

    DrMaxP

    Joined:
    Sep 18, 2018
    Posts:
    29
    Is there anything built in that lets me add a class level description to a class that is visible in the inspector - i.e not at the property level but at the class level?
     
    Roman_Keivan likes this.
  17. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    This is a hack, but you could add the HelpBox attribute to the first serialized property in your class so it appears at the top of the inspector, and put your class level description in it.

    Otherwise I think you'll need to write a custom inspector. A short one would do. Something like:

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [CustomEditor(typeof(MyClass), true)]
    5. [CanEditMultipleObjects]
    6. public class MyClassEditor : Editor {
    7.     public override void OnInspectorGUI() {
    8.         EditorGUILayout.HelpBox("Class level info.", MessageType.None);
    9.         DrawDefaultInspector();
    10.     }
    11. }
     
    Roman_Keivan likes this.
  18. AlanMattano

    AlanMattano

    Joined:
    Aug 22, 2013
    Posts:
    1,501
  19. MrMatthias

    MrMatthias

    Joined:
    Sep 18, 2012
    Posts:
    191
    Nice, you could add a button with a question mark that opens and closes the help
     
    Roman_Keivan likes this.
  20. share_gamerz_ID

    share_gamerz_ID

    Joined:
    Mar 19, 2019
    Posts:
    12
    Such a nice script. Loved it. simple and easy.
     
    Roman_Keivan likes this.
  21. KarlKarl2000

    KarlKarl2000

    Joined:
    Jan 25, 2016
    Posts:
    606
    @usergame
    I've been using this script for a while now. It's wonderful!

    Thanks for this fun script.

    I ran into a snag recently however.

    I can't figure out how to add it to my assembly definition. Do you have any advice?

    Thank you!:rolleyes:
     
    Roman_Keivan likes this.
  22. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    A bug in Unity 2022.2 calls drawers' GetHeight() outside of IMGUI's repaint pass, which will cause GUI* calls to throw an ArgumentException. To work around this, you can check what pass IMGUI is in or just catch the exception:

    Code (csharp):
    1. public override float GetHeight() {
    2.     try {
    3.         var helpBoxAttribute = attribute as HelpBoxAttribute;
    4.         if (helpBoxAttribute == null) return base.GetHeight();
    5.         var helpBoxStyle = (GUI.skin != null) ? GUI.skin.GetStyle("helpbox") : null;
    6.         if (helpBoxStyle == null) return base.GetHeight();
    7.         return Mathf.Max(40f, helpBoxStyle.CalcHeight(new GUIContent(helpBoxAttribute.text), EditorGUIUtility.currentViewWidth) + 4);
    8.     }
    9.     catch (System.ArgumentException) {
    10.         return 3 * EditorGUIUtility.singleLineHeight; // Handle Unity 2022.2 bug by returning default value.
    11.     }
    12. }
     
    onetimepad and rockin like this.
  23. rockin

    rockin

    Joined:
    Dec 11, 2013
    Posts:
    26
    @TonyLi thanks for this. I also noticed that the info icon takes some space and that has to be accounted for in the line height calculation. In my tests on my normal monitor (non-hi DPI), the icon seems to take 67 units. Here's what I did:

    Code (CSharp):
    1.  
    2.                 // Available width is smaller when showing an icon in the box.
    3.                 var widthModifier = helpBoxAttribute.MessageType == HelpBoxMessageType.None ? 0 : -67;
    4.  
    5.                 return Mathf.Max(40f, helpBoxStyle.CalcHeight(new GUIContent(helpBoxAttribute.Text),
    6.                     EditorGUIUtility.currentViewWidth + widthModifier) + 4);
    7.  
     
    TonyLi likes this.