1- // References:
1+ // References:
22// https://gist.github.com/tomkail/ba4136e6aa990f4dc94e0d39ec6a058c
33// Developed by Tom Kail at Inkle
44// Released under the MIT Licence as held at https://opensource.org/licenses/MIT
55
66using System ;
77using System . Linq ;
8+ using System . Collections . Generic ;
89using UnityEngine ;
910using UnityEditor ;
10- using Object = UnityEngine . Object ;
1111
1212namespace ScriptableData . Editor
1313{
14- [ CustomPropertyDrawer ( typeof ( ScriptableObject ) , true ) ]
15- public class ExtendedScriptableObjectDrawer : PropertyDrawer
16- {
17- private static readonly string [ ] ignoreClassFullNames = new string [ ] { "TMPro.TMP_FontAsset" } ;
18- private const int buttonWidth = 20 ;
19-
20- public override float GetPropertyHeight ( SerializedProperty property , GUIContent label )
21- {
22- float totalHeight = EditorGUIUtility . singleLineHeight ;
23- if ( property . objectReferenceValue == null || ! HasVisableSubProperties ( property ) || fieldInfo . HasAttribute < NonExtendableAttribute > ( ) )
24- {
25- return totalHeight ;
26- }
27-
28- if ( property . isExpanded )
29- {
30- ScriptableObject data = property . objectReferenceValue as ScriptableObject ;
31-
32- if ( data == null )
33- {
34- return EditorGUIUtility . singleLineHeight ;
35- }
36-
37- SerializedObject serializedObject = new SerializedObject ( data ) ;
38- SerializedProperty prop = serializedObject . GetIterator ( ) ;
39- if ( prop . NextVisible ( true ) )
40- {
41- do
42- {
43- if ( prop . name == "m_Script" )
44- {
45- continue ;
46- }
47-
48- SerializedProperty subProp = serializedObject . FindProperty ( prop . name ) ;
49- float height = EditorGUI . GetPropertyHeight ( subProp , null , true ) + EditorGUIUtility . standardVerticalSpacing ;
50- totalHeight += height ;
51- }
52- while ( prop . NextVisible ( false ) ) ;
53- }
54- // Add a tiny bit of height if open for the background
55- totalHeight += EditorGUIUtility . standardVerticalSpacing ;
56- }
57-
58- return totalHeight ;
59- }
60-
61- public override void OnGUI ( Rect position , SerializedProperty property , GUIContent label )
62- {
63- EditorGUI . BeginProperty ( position , label , property ) ;
14+ [ CustomPropertyDrawer ( typeof ( ScriptableObject ) , true ) ]
15+ public class ExtendedScriptableObjectDrawer : PropertyDrawer
16+ {
17+ // Permamently exclude classes from being affected by the drawer.
18+ private static readonly string [ ] ignoredFullClassNames = new string [ ] { "TMPro.TMP_FontAsset" } ;
19+ private const int buttonWidth = 20 ;
20+
21+ public override float GetPropertyHeight ( SerializedProperty property , GUIContent label )
22+ {
23+ float totalHeight = EditorGUIUtility . singleLineHeight ;
24+
25+ SerializedObject serializedObject = null ;
26+ if ( property . objectReferenceValue is ScriptableObject scriptableObject )
27+ {
28+ serializedObject = new SerializedObject ( scriptableObject ) ;
29+ }
30+
31+ if ( serializedObject == null || ! serializedObject . HasVisableSubProperties ( ) || fieldInfo . HasAttribute < NonExtendableAttribute > ( ) )
32+ {
33+ return totalHeight ;
34+ }
35+
36+ if ( property . isExpanded )
37+ {
38+ // Iterate over all the values and draw them.
39+ SerializedProperty p = serializedObject . GetIterator ( ) ;
40+
41+ if ( p . NextVisible ( true ) )
42+ {
43+ do
44+ {
45+ // Skip drawing the class file.
46+ if ( EditorUtils . IsIgnoredProperty ( p ) )
47+ {
48+ continue ;
49+ }
50+
51+ float height = EditorGUI . GetPropertyHeight ( p , null , true ) + EditorGUIUtility . standardVerticalSpacing ;
52+ totalHeight += height ;
53+ }
54+ while ( p . NextVisible ( false ) ) ;
55+ }
56+
57+ // Add a tiny bit of height if open for the background
58+ totalHeight += EditorGUIUtility . standardVerticalSpacing ;
59+ }
60+
61+ return totalHeight ;
62+ }
63+
64+ public override void OnGUI ( Rect position , SerializedProperty property , GUIContent label )
65+ {
66+ EditorGUI . BeginProperty ( position , label , property ) ;
6467
6568 Type fieldType = EditorUtils . GetFieldType ( fieldInfo ) ;
6669
67- if ( fieldType == null || HasIgnoredClassName ( fieldType ) || fieldInfo . HasAttribute < NonExtendableAttribute > ( ) )
68- {
69- EditorGUI . PropertyField ( position , property , label ) ;
70- EditorGUI . EndProperty ( ) ;
71- return ;
72- }
70+ // Draw with default drawer.
71+ if ( fieldType == null || HasIgnoredClassName ( fieldType ) || fieldInfo . HasAttribute < NonExtendableAttribute > ( ) )
72+ {
73+ EditorGUI . PropertyField ( position , property , label ) ;
74+ EditorGUI . EndProperty ( ) ;
75+ return ;
76+ }
7377
74- ScriptableObject propertySO = null ;
75- if ( ! property . hasMultipleDifferentValues && property . serializedObject . targetObject != null && property . serializedObject . targetObject is ScriptableObject )
76- {
77- propertySO = property . serializedObject . targetObject as ScriptableObject ;
78- }
79-
80- GUIContent guiContent = new GUIContent ( property . displayName ) ;
8178 Rect foldoutRect = new Rect ( position . x , position . y , EditorGUIUtility . labelWidth , EditorGUIUtility . singleLineHeight ) ;
82- if ( property . objectReferenceValue != null && HasVisableSubProperties ( property ) )
83- {
84- property . isExpanded = EditorGUI . Foldout ( foldoutRect , property . isExpanded , guiContent , true ) ;
85- }
86- else
87- {
88- foldoutRect . x += 10 ;
89- EditorGUI . Foldout ( foldoutRect , property . isExpanded , guiContent , true , EditorStyles . label ) ;
90- }
91-
9279 Rect indentedPosition = EditorGUI . IndentedRect ( position ) ;
9380 float indentOffset = indentedPosition . x - position . x ;
94- Rect propertyRect = new Rect ( position . x + ( EditorGUIUtility . labelWidth - indentOffset + 2 ) , position . y , position . width - ( EditorGUIUtility . labelWidth - indentOffset + 2 ) , EditorGUIUtility . singleLineHeight ) ;
95-
96- if ( propertySO != null || property . objectReferenceValue == null )
97- {
98- propertyRect . width -= buttonWidth ;
99- }
100-
101- EditorGUI . BeginChangeCheck ( ) ;
102-
103- Object assignedObject = EditorGUI . ObjectField ( propertyRect , GUIContent . none , property . objectReferenceValue , fieldType , false ) ;
104-
105- if ( EditorGUI . EndChangeCheck ( ) )
106- {
107- property . objectReferenceValue = assignedObject ;
108- property . serializedObject . ApplyModifiedProperties ( ) ;
109- }
110-
111- Rect buttonRect = new Rect ( position . x + position . width - buttonWidth , position . y , buttonWidth , EditorGUIUtility . singleLineHeight ) ;
112-
113- if ( property . propertyType == SerializedPropertyType . ObjectReference && property . objectReferenceValue != null )
114- {
115- if ( property . isExpanded )
116- {
117- DrawScriptableObjectChildFields ( position , property ) ;
118- }
119- }
120- else
121- {
122- DrawScriptableObjectCreateButton ( buttonRect , property , fieldType ) ;
123- }
124-
125- property . serializedObject . ApplyModifiedProperties ( ) ;
126- EditorGUI . EndProperty ( ) ;
127- }
128-
129- private void DrawScriptableObjectChildFields ( Rect position , SerializedProperty property )
81+ Rect propertyRect = new Rect ( position . x + ( EditorGUIUtility . labelWidth - indentOffset + 2 ) , position . y , position . width - ( EditorGUIUtility . labelWidth - indentOffset + 2 ) , EditorGUIUtility . singleLineHeight ) ;
82+
83+ SerializedObject serializedObject = null ;
84+ if ( property . objectReferenceValue is ScriptableObject scriptableObject )
85+ {
86+ serializedObject = new SerializedObject ( scriptableObject ) ;
87+ }
88+
89+ // Check for sub properties and if the property can be expanded.
90+ if ( serializedObject != null && serializedObject . HasVisableSubProperties ( ) )
91+ {
92+ property . isExpanded = EditorGUI . Foldout ( foldoutRect , property . isExpanded , new GUIContent ( property . displayName ) , true ) ;
93+
94+ if ( property . isExpanded )
95+ {
96+ // Draw sub properties.
97+ serializedObject . Draw ( position ) ;
98+ }
99+ }
100+ else
101+ {
102+ EditorGUI . LabelField ( foldoutRect , new GUIContent ( property . displayName ) ) ;
103+
104+ if ( property . objectReferenceValue == null )
105+ {
106+ propertyRect . width -= buttonWidth ;
107+
108+ // Draw create button.
109+ Rect buttonRect = new Rect ( position . x + position . width - buttonWidth , position . y , buttonWidth , EditorGUIUtility . singleLineHeight ) ;
110+ DrawScriptableObjectCreateButton ( buttonRect , property , fieldType ) ;
111+ }
112+ }
113+
114+ EditorGUI . BeginChangeCheck ( ) ;
115+ // Draw object field.
116+ EditorGUI . PropertyField ( propertyRect , property , GUIContent . none , false ) ;
117+
118+ if ( EditorGUI . EndChangeCheck ( ) )
119+ {
120+ property . serializedObject . ApplyModifiedProperties ( ) ;
121+ }
122+
123+ property . serializedObject . ApplyModifiedProperties ( ) ;
124+ EditorGUI . EndProperty ( ) ;
125+ }
126+
127+ private static void DrawScriptableObjectCreateButton ( Rect position , SerializedProperty property , Type type )
130128 {
131- ScriptableObject scriptableObject = ( ScriptableObject ) property . objectReferenceValue ;
132-
133- if ( scriptableObject == null )
134- {
135- return ;
136- }
137-
138- // Draw a background that shows us clearly which fields are part of the ScriptableObject
139- GUI . Box ( new Rect ( position . x , position . y + EditorGUIUtility . singleLineHeight + EditorGUIUtility . standardVerticalSpacing - 1 , position . width , position . height - EditorGUIUtility . singleLineHeight - EditorGUIUtility . standardVerticalSpacing ) , "" ) ;
140-
141- EditorGUI . indentLevel ++ ;
142- SerializedObject serializedObject = new SerializedObject ( scriptableObject ) ;
143-
144- // Iterate over all the values and draw them
145- SerializedProperty prop = serializedObject . GetIterator ( ) ;
146- float y = position . y + EditorGUIUtility . singleLineHeight + EditorGUIUtility . standardVerticalSpacing ;
147-
148- EditorGUI . BeginChangeCheck ( ) ;
149-
150- if ( prop . NextVisible ( true ) )
151- {
152- do
153- {
154- // Don't bother drawing the class file
155- if ( prop . name == "m_Script" ) continue ;
156- float height = EditorGUI . GetPropertyHeight ( prop , new GUIContent ( prop . displayName ) , true ) ;
157- EditorGUI . PropertyField ( new Rect ( position . x , y , position . width /*- buttonWidth*/ , height ) , prop , true ) ;
158- y += height + EditorGUIUtility . standardVerticalSpacing ;
159- }
160- while ( prop . NextVisible ( false ) ) ;
161- }
162-
163- if ( EditorGUI . EndChangeCheck ( ) )
164- {
165- serializedObject . ApplyModifiedProperties ( ) ;
166- }
167-
168- EditorGUI . indentLevel -- ;
169- }
170-
171- private void DrawScriptableObjectCreateButton ( Rect position , SerializedProperty property , Type type )
172- {
173- if ( GUI . Button ( position , "+" ) )
174- {
175- if ( type . IsAbstract )
176- {
177- GenericMenu typeChooser = new GenericMenu ( ) ;
178- foreach ( var elem in type . Assembly . GetTypes ( ) . Where ( t => type . IsAssignableFrom ( t ) ) )
179- {
180- if ( elem . IsAbstract ) continue ;
181-
182- typeChooser . AddItem ( new GUIContent ( elem . Name ) , false , ( elem ) => {
183- property . objectReferenceValue = EditorUtils . CreateAssetWithSavePrompt ( elem as Type , EditorUtils . GetSelectedAssetPath ( property ) ) ;
184- property . serializedObject . ApplyModifiedProperties ( ) ;
185- } , elem ) ;
186- }
187- typeChooser . ShowAsContext ( ) ;
188- }
189- else
190- {
191- property . objectReferenceValue = EditorUtils . CreateAssetWithSavePrompt ( type , EditorUtils . GetSelectedAssetPath ( property ) ) ;
192- }
193- }
194- }
195-
196- private bool HasIgnoredClassName ( Type type )
197- {
198- return ignoreClassFullNames . Contains ( type . FullName ) ;
199- }
200-
201- private bool HasVisableSubProperties ( SerializedProperty property )
129+ if ( GUI . Button ( position , "+" ) )
130+ {
131+ GenericMenu typeChooser = new GenericMenu ( ) ;
132+ IEnumerable < Type > types = type . Assembly . GetTypes ( ) . Where ( t => type . IsAssignableFrom ( t ) ) ;
133+ foreach ( Type t in types )
134+ {
135+ if ( t . IsAbstract )
136+ {
137+ continue ;
138+ }
139+
140+ typeChooser . AddItem ( new GUIContent ( t . Name ) , false , ( o ) => {
141+ property . objectReferenceValue = EditorUtils . CreateAssetWithSavePrompt ( o as Type , EditorUtils . GetSelectedAssetPath ( property ) ) ;
142+ property . serializedObject . ApplyModifiedProperties ( ) ;
143+ } , t ) ;
144+ }
145+ typeChooser . ShowAsContext ( ) ;
146+ }
147+ }
148+
149+ private static bool HasIgnoredClassName ( Type type )
202150 {
203- ScriptableObject scriptableObject = ( ScriptableObject ) property . objectReferenceValue ;
204-
205- if ( scriptableObject != null )
206- {
207- SerializedObject serializedObject = new SerializedObject ( scriptableObject ) ;
208- SerializedProperty prop = serializedObject . GetIterator ( ) ;
209- while ( prop . NextVisible ( true ) )
210- {
211- if ( prop . name == "m_Script" ) continue ;
212- return true ; //if theres any visible property other than m_script
213- }
214- }
215- return false ;
216- }
217- }
151+ return ignoredFullClassNames . Contains ( type . FullName ) ;
152+ }
153+ }
218154}
0 commit comments