Skip to content

Commit f1c2670

Browse files
committed
adding a helpbox
1 parent f087e16 commit f1c2670

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

HelpBox/HelpBoxAttribute.cs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
using System;
2+
using UnityEngine;
3+
#if UNITY_EDITOR
4+
using UnityEditor;
5+
#endif
6+
7+
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
8+
public class HelpAttribute : PropertyAttribute
9+
{
10+
public readonly string text;
11+
12+
// MessageType exists in UnityEditor namespace and can throw an exception when used outside the editor.
13+
// We spoof MessageType at the bottom of this script to ensure that errors are not thrown when
14+
// MessageType is unavailable.
15+
public readonly MessageType type;
16+
17+
18+
/// <summary>
19+
/// Adds a HelpBox to the Unity property inspector above this field.
20+
/// </summary>
21+
/// <param name="text">The help text to be displayed in the HelpBox.</param>
22+
/// <param name="type">The icon to be displayed in the HelpBox.</param>
23+
public HelpAttribute(string text, MessageType type = MessageType.Info)
24+
{
25+
this.text = text;
26+
this.type = type;
27+
}
28+
}
29+
30+
#if UNITY_EDITOR
31+
[CustomPropertyDrawer(typeof(HelpAttribute))]
32+
public class HelpDrawer : PropertyDrawer
33+
{
34+
// Used for top and bottom padding between the text and the HelpBox border.
35+
const int paddingHeight = 8;
36+
37+
// Used to add some margin between the the HelpBox and the property.
38+
const int marginHeight = 2;
39+
40+
// Global field to store the original (base) property height.
41+
float baseHeight = 0;
42+
43+
// Custom added height for drawing text area which has the MultilineAttribute.
44+
float addedHeight = 0;
45+
46+
/// <summary>
47+
/// A wrapper which returns the PropertyDrawer.attribute field as a HelpAttribute.
48+
/// </summary>
49+
HelpAttribute helpAttribute { get { return (HelpAttribute)attribute; } }
50+
51+
/// <summary>
52+
/// A helper property to check for RangeAttribute.
53+
/// </summary>
54+
RangeAttribute rangeAttribute
55+
{
56+
get
57+
{
58+
var attributes = fieldInfo.GetCustomAttributes(typeof(RangeAttribute), true);
59+
return attributes != null && attributes.Length > 0 ? (RangeAttribute)attributes[0] : null;
60+
}
61+
}
62+
63+
/// <summary>
64+
/// A helper property to check for MultiLineAttribute.
65+
/// </summary>
66+
MultilineAttribute multilineAttribute
67+
{
68+
get
69+
{
70+
var attributes = fieldInfo.GetCustomAttributes(typeof(MultilineAttribute), true);
71+
return attributes != null && attributes.Length > 0 ? (MultilineAttribute)attributes[0] : null;
72+
}
73+
}
74+
75+
76+
public override float GetPropertyHeight(SerializedProperty prop, GUIContent label)
77+
{
78+
// We store the original property height for later use...
79+
baseHeight = base.GetPropertyHeight(prop, label);
80+
81+
// This stops icon shrinking if text content doesn't fill out the container enough.
82+
float minHeight = paddingHeight * 5;
83+
84+
// Calculate the height of the HelpBox using the GUIStyle on the current skin and the inspector
85+
// window's currentViewWidth.
86+
var content = new GUIContent(helpAttribute.text);
87+
var style = GUI.skin.GetStyle("helpbox");
88+
89+
var height = style.CalcHeight(content, EditorGUIUtility.currentViewWidth);
90+
91+
// We add tiny padding here to make sure the text is not overflowing the HelpBox from the top
92+
// and bottom.
93+
height += marginHeight * 2;
94+
95+
// Since we draw a custom text area with the label above if our property contains the
96+
// MultilineAttribute, we need to add some extra height to compensate. This is stored in a
97+
// seperate global field so we can use it again later.
98+
if (multilineAttribute != null && prop.propertyType == SerializedPropertyType.String)
99+
{
100+
addedHeight = 48f;
101+
}
102+
103+
// If the calculated HelpBox is less than our minimum height we use this to calculate the returned
104+
// height instead.
105+
return height > minHeight ? height + baseHeight + addedHeight : minHeight + baseHeight + addedHeight;
106+
}
107+
108+
109+
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
110+
{
111+
// We get a local reference to the MultilineAttribute as we use it twice in this method and it
112+
// saves calling the logic twice for minimal optimization, etc...
113+
var multiline = multilineAttribute;
114+
115+
EditorGUI.BeginProperty(position, label, prop);
116+
117+
// Copy the position out so we can calculate the position of our HelpBox without affecting the
118+
// original position.
119+
var helpPos = position;
120+
121+
helpPos.height -= baseHeight + marginHeight;
122+
123+
124+
if (multiline != null)
125+
{
126+
helpPos.height -= addedHeight;
127+
}
128+
129+
// Renders the HelpBox in the Unity inspector UI.
130+
EditorGUI.HelpBox(helpPos, helpAttribute.text, helpAttribute.type);
131+
132+
position.y += helpPos.height + marginHeight;
133+
position.height = baseHeight;
134+
135+
136+
// If we have a RangeAttribute on our field, we need to handle the PropertyDrawer differently to
137+
// keep the same style as Unity's default.
138+
var range = rangeAttribute;
139+
140+
if (range != null)
141+
{
142+
if (prop.propertyType == SerializedPropertyType.Float)
143+
{
144+
EditorGUI.Slider(position, prop, range.min, range.max, label);
145+
}
146+
else if (prop.propertyType == SerializedPropertyType.Integer)
147+
{
148+
EditorGUI.IntSlider(position, prop, (int)range.min, (int)range.max, label);
149+
}
150+
else
151+
{
152+
// Not numeric so draw standard property field as punishment for adding RangeAttribute to
153+
// a property which can not have a range :P
154+
EditorGUI.PropertyField(position, prop, label);
155+
}
156+
}
157+
else if (multiline != null)
158+
{
159+
// Here's where we handle the PropertyDrawer differently if we have a MultiLineAttribute, to try
160+
// and keep some kind of multiline text area. This is not identical to Unity's default but is
161+
// better than nothing...
162+
if (prop.propertyType == SerializedPropertyType.String)
163+
{
164+
var style = GUI.skin.label;
165+
var size = style.CalcHeight(label, EditorGUIUtility.currentViewWidth);
166+
167+
EditorGUI.LabelField(position, label);
168+
169+
position.y += size;
170+
position.height += addedHeight - size;
171+
172+
// Fixed text dissappearing thanks to: http://answers.unity3d.com/questions/244043/textarea-does-not-work-text-dissapears-solution-is.html
173+
prop.stringValue = EditorGUI.TextArea(position, prop.stringValue);
174+
}
175+
else
176+
{
177+
// Again with a MultilineAttribute on a non-text field deserves for the standard property field
178+
// to be drawn as punishment :P
179+
EditorGUI.PropertyField(position, prop, label);
180+
}
181+
}
182+
else
183+
{
184+
// If we get to here it means we're drawing the default property field below the HelpBox. More custom
185+
// and built in PropertyDrawers could be implemented to enable HelpBox but it could easily make for
186+
// hefty else/if block which would need refactoring!
187+
EditorGUI.PropertyField(position, prop, label);
188+
}
189+
190+
EditorGUI.EndProperty();
191+
}
192+
}
193+
#else
194+
// Replicate MessageType Enum if we are not in editor as this enum exists in UnityEditor namespace.
195+
// This should stop errors being logged the same as Shawn Featherly's commit in the Github repo but I
196+
// feel is cleaner than having the conditional directive in the middle of the HelpAttribute constructor.
197+
public enum MessageType
198+
{
199+
None,
200+
Info,
201+
Warning,
202+
Error,
203+
}
204+
#endif

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,31 @@ To convert any field into a ReadOnly column:
1212

1313
````csharp
1414
[ReadOnly] public float example;
15+
````
16+
17+
## Help Box
18+
19+
_**Source:** [unity-inspector-help](https://github.com/johnearnshaw/unity-inspector-help/tree/master)_
20+
21+
Add a help text box info into your inspector.
22+
23+
By default, you can just use:
24+
25+
````csharp
26+
[Help("This is some help text!")]
27+
public float example = 1000f;
28+
````
29+
30+
You can add an optional parameter that changes the icon that appears with the help box:
31+
32+
* `UnityEditor.MessageType.Info` : The default icon.
33+
* `UnityEditor.MessageType.Error` : A red error icon.
34+
* `UnityEditor.MessageType.Warning` : A yellow warning icon.
35+
* `UnityEditor.MessageType.None` : Removes the icon, leaving only the text.
36+
37+
````csharp
38+
#if UNITY_EDITOR
39+
[Help("This is some help text!", UnityEditor.MessageType.None)]
40+
#endif
41+
public float example = 1000f;
1542
````

0 commit comments

Comments
 (0)