-
Notifications
You must be signed in to change notification settings - Fork 21
/
HelpAttribute.cs
230 lines (195 loc) · 9.51 KB
/
HelpAttribute.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// --------------------------------------------------------------------------------------------------------------------
/// <copyright file="HelpAttribute.cs">
/// <See cref="https://github.com/johnearnshaw/unity-inspector-help"></See>
/// Copyright (c) 2017, John Earnshaw, reblGreen Software Limited
/// <See cref="https://github.com/johnearnshaw/"></See>
/// <See cref="https://bitbucket.com/juanshaf/"></See>
/// <See cref="https://reblgreen.com/"></See>
/// All rights reserved.
/// Redistribution and use in source and binary forms, with or without modification, are
/// permitted provided that the following conditions are met:
/// 1. Redistributions of source code must retain the above copyright notice, this list of
/// conditions and the following disclaimer.
/// 2. Redistributions in binary form must reproduce the above copyright notice, this list
/// of conditions and the following disclaimer in the documentation and/or other materials
/// provided with the distribution.
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
/// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
/// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE
/// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
/// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
/// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
/// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
/// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
/// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
public class HelpAttribute : PropertyAttribute
{
public readonly string text;
// MessageType exists in UnityEditor namespace and can throw an exception when used outside the editor.
// We spoof MessageType at the bottom of this script to ensure that errors are not thrown when
// MessageType is unavailable.
public readonly MessageType type;
/// <summary>
/// Adds a HelpBox to the Unity property inspector above this field.
/// </summary>
/// <param name="text">The help text to be displayed in the HelpBox.</param>
/// <param name="type">The icon to be displayed in the HelpBox.</param>
public HelpAttribute(string text, MessageType type = MessageType.Info)
{
this.text = text;
this.type = type;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(HelpAttribute))]
public class HelpDrawer : PropertyDrawer
{
// Used for top and bottom padding between the text and the HelpBox border.
const int paddingHeight = 8;
// Used to add some margin between the the HelpBox and the property.
const int marginHeight = 2;
// Global field to store the original (base) property height.
float baseHeight = 0;
// Custom added height for drawing text area which has the MultilineAttribute.
float addedHeight = 0;
/// <summary>
/// A wrapper which returns the PropertyDrawer.attribute field as a HelpAttribute.
/// </summary>
HelpAttribute helpAttribute { get { return (HelpAttribute)attribute; } }
/// <summary>
/// A helper property to check for RangeAttribute.
/// </summary>
RangeAttribute rangeAttribute
{
get
{
var attributes = fieldInfo.GetCustomAttributes(typeof(RangeAttribute), true);
return attributes != null && attributes.Length > 0 ? (RangeAttribute)attributes[0] : null;
}
}
/// <summary>
/// A helper property to check for MultiLineAttribute.
/// </summary>
MultilineAttribute multilineAttribute
{
get
{
var attributes = fieldInfo.GetCustomAttributes(typeof(MultilineAttribute), true);
return attributes != null && attributes.Length > 0 ? (MultilineAttribute)attributes[0] : null;
}
}
public override float GetPropertyHeight(SerializedProperty prop, GUIContent label)
{
// We store the original property height for later use...
baseHeight = base.GetPropertyHeight(prop, label);
// This stops icon shrinking if text content doesn't fill out the container enough.
float minHeight = paddingHeight * 5;
// Calculate the height of the HelpBox using the GUIStyle on the current skin and the inspector
// window's currentViewWidth.
var content = new GUIContent(helpAttribute.text);
var style = GUI.skin.GetStyle("helpbox");
var height = style.CalcHeight(content, EditorGUIUtility.currentViewWidth);
// We add tiny padding here to make sure the text is not overflowing the HelpBox from the top
// and bottom.
height += marginHeight * 2;
// Since we draw a custom text area with the label above if our property contains the
// MultilineAttribute, we need to add some extra height to compensate. This is stored in a
// seperate global field so we can use it again later.
if (multilineAttribute != null && prop.propertyType == SerializedPropertyType.String)
{
addedHeight = 48f;
}
// If the calculated HelpBox is less than our minimum height we use this to calculate the returned
// height instead.
return height > minHeight ? height + baseHeight + addedHeight : minHeight + baseHeight + addedHeight;
}
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
{
// We get a local reference to the MultilineAttribute as we use it twice in this method and it
// saves calling the logic twice for minimal optimization, etc...
var multiline = multilineAttribute;
EditorGUI.BeginProperty(position, label, prop);
// Copy the position out so we can calculate the position of our HelpBox without affecting the
// original position.
var helpPos = position;
helpPos.height -= baseHeight + marginHeight;
if (multiline != null)
{
helpPos.height -= addedHeight;
}
// Renders the HelpBox in the Unity inspector UI.
EditorGUI.HelpBox(helpPos, helpAttribute.text, helpAttribute.type);
position.y += helpPos.height + marginHeight;
position.height = baseHeight;
// If we have a RangeAttribute on our field, we need to handle the PropertyDrawer differently to
// keep the same style as Unity's default.
var range = rangeAttribute;
if (range != null)
{
if (prop.propertyType == SerializedPropertyType.Float)
{
EditorGUI.Slider(position, prop, range.min, range.max, label);
}
else if (prop.propertyType == SerializedPropertyType.Integer)
{
EditorGUI.IntSlider(position, prop, (int)range.min, (int)range.max, label);
}
else
{
// Not numeric so draw standard property field as punishment for adding RangeAttribute to
// a property which can not have a range :P
EditorGUI.PropertyField(position, prop, label);
}
}
else if (multiline != null)
{
// Here's where we handle the PropertyDrawer differently if we have a MultiLineAttribute, to try
// and keep some kind of multiline text area. This is not identical to Unity's default but is
// better than nothing...
if (prop.propertyType == SerializedPropertyType.String)
{
var style = GUI.skin.label;
var size = style.CalcHeight(label, EditorGUIUtility.currentViewWidth);
EditorGUI.LabelField(position, label);
position.y += size;
position.height += addedHeight - size;
// Fixed text dissappearing thanks to: http://answers.unity3d.com/questions/244043/textarea-does-not-work-text-dissapears-solution-is.html
prop.stringValue = EditorGUI.TextArea(position, prop.stringValue);
}
else
{
// Again with a MultilineAttribute on a non-text field deserves for the standard property field
// to be drawn as punishment :P
EditorGUI.PropertyField(position, prop, label);
}
}
else
{
// If we get to here it means we're drawing the default property field below the HelpBox. More custom
// and built in PropertyDrawers could be implemented to enable HelpBox but it could easily make for
// hefty else/if block which would need refactoring!
EditorGUI.PropertyField(position, prop, label);
}
EditorGUI.EndProperty();
}
}
#else
// Replicate MessageType Enum if we are not in editor as this enum exists in UnityEditor namespace.
// This should stop errors being logged the same as Shawn Featherly's commit in the Github repo but I
// feel is cleaner than having the conditional directive in the middle of the HelpAttribute constructor.
public enum MessageType
{
None,
Info,
Warning,
Error,
}
#endif