Tanoda
CustomEditorBase.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using UnityEngine;
10using UnityEditor;
11using System;
12using System.Collections.Generic;
13using Leap.Unity.Query;
14
15namespace Leap.Unity {
16
18 protected new T target;
19 protected new T[] targets;
20
21 protected override void OnEnable() {
22 base.OnEnable();
23
24 target = base.target as T;
25 targets = base.targets.Query().
26 Where(t => t != null).
27 OfType<T>().
28 ToArray();
29 }
30 }
31
32 public class CustomEditorBase : Editor {
33 protected Dictionary<string, Action<SerializedProperty >> _specifiedDrawers;
34 protected Dictionary<string, List<Action<SerializedProperty>>> _specifiedDecorators;
35 protected Dictionary<string, List<Action<SerializedProperty>>> _specifiedPostDecorators;
36 protected Dictionary<string, List<Func<bool>>> _conditionalProperties;
37 protected Dictionary<string, List<string>> _foldoutProperties;
38 protected Dictionary<string, bool> _foldoutStates;
39 protected List <string > _deferredProperties;
40 protected bool _showScriptField = true;
41
42 private bool _canCallSpecifyFunctions = false;
43 private GUIStyle _boldFoldoutStyle;
44
45 protected List<SerializedProperty> _modifiedProperties = new List<SerializedProperty>();
46
47 protected void dontShowScriptField() {
48 _showScriptField = false;
49 }
50
56 protected void specifyCustomDrawer(string propertyName, Action<SerializedProperty> propertyDrawer) {
57 throwIfNotInOnEnable("specifyCustomDrawer");
58
59 if (!validateProperty(propertyName)) {
60 return;
61 }
62
63 _specifiedDrawers[propertyName] = propertyDrawer;
64 }
65
69 protected void specifyCustomDecorator(string propertyName, Action<SerializedProperty> decoratorDrawer) {
70 throwIfNotInOnEnable("specifyCustomDecorator");
71
72 if (!validateProperty(propertyName)) {
73 return;
74 }
75
76 List<Action<SerializedProperty>> list;
77 if (!_specifiedDecorators.TryGetValue(propertyName, out list)) {
78 list = new List<Action<SerializedProperty>>();
79 _specifiedDecorators[propertyName] = list;
80 }
81
82 list.Add(decoratorDrawer);
83 }
84
90 protected void specifyCustomPostDecorator(string propertyName, Action<SerializedProperty> decoratorDrawer) {
91 throwIfNotInOnEnable("specifyCustomPostDecorator");
92
93 if (!validateProperty(propertyName)) {
94 return;
95 }
96
97 List<Action<SerializedProperty>> list;
98 if (!_specifiedPostDecorators.TryGetValue(propertyName, out list)) {
99 list = new List<Action<SerializedProperty>>();
100 _specifiedPostDecorators[propertyName] = list;
101 }
102
103 list.Add(decoratorDrawer);
104 }
105
112 protected void specifyConditionalDrawing(string conditionalName, params string[] dependantProperties) {
113 throwIfNotInOnEnable("specifyConditionalDrawing");
114
115 if (!validateProperty(conditionalName)) {
116 return;
117 }
118
119 SerializedProperty conditionalProp = serializedObject.FindProperty(conditionalName);
121 if (conditionalProp.hasMultipleDifferentValues) {
122 return false;
123 } else {
124 return conditionalProp.boolValue;
125 }
126 }, dependantProperties);
127 }
128
129 protected void specifyConditionalDrawing(string enumName, int enumValue, params string[] dependantProperties) {
130 throwIfNotInOnEnable("specifyConditionalDrawing");
131
132 if (!validateProperty(enumName)) {
133 return;
134 }
135
136 SerializedProperty enumProp = serializedObject.FindProperty(enumName);
138 if (enumProp.hasMultipleDifferentValues) {
139 return false;
140 } else {
141 return enumProp.intValue == enumValue;
142 }
143 }, dependantProperties);
144 }
145
146 protected void hideField(string propertyName) {
147 throwIfNotInOnEnable("hideField");
148
149 specifyConditionalDrawing(() => false, propertyName);
150 }
151
152 protected void specifyConditionalDrawing(Func<bool> conditional, params string[] dependantProperties) {
153 throwIfNotInOnEnable("specifyConditionalDrawing");
154
155 for (int i = 0; i < dependantProperties.Length; i++) {
156 string dependant = dependantProperties[i];
157
158 if (!validateProperty(dependant)) {
159 continue;
160 }
161
162 List<Func<bool>> list;
163 if (!_conditionalProperties.TryGetValue(dependant, out list)) {
164 list = new List<Func<bool>>();
165 _conditionalProperties[dependant] = list;
166 }
167 list.Add(conditional);
168 }
169 }
170
176 protected void deferProperty(string propertyName) {
177 throwIfNotInOnEnable("deferProperty");
178
179 if (!validateProperty(propertyName)) {
180 return;
181 }
182
183 _deferredProperties.Insert(0, propertyName);
184 }
185
189 protected void addPropertyToFoldout(string propertyName, string foldoutName, bool foldoutStartOpen = false) {
190 throwIfNotInOnEnable("addPropertyToFoldout");
191
192 if (!validateProperty(propertyName)) { return; }
193
194 List<string> list;
195 if (!_foldoutProperties.TryGetValue(foldoutName, out list)) {
196 list = new List<string>();
197 _foldoutProperties[foldoutName] = list;
198 }
199 _foldoutProperties[foldoutName].Add(propertyName);
200 _foldoutStates [foldoutName] = foldoutStartOpen;
201 }
202
206 protected bool isInFoldout(string propertyName) {
207 bool isInFoldout = false;
208 foreach (var foldout in _foldoutProperties) {
209 foreach (string property in foldout.Value) {
210 if (property.Equals(propertyName)) { isInFoldout = true; break; }
211 }
212 if (isInFoldout) { break; }
213 }
214 return isInFoldout;
215 }
216
217 protected void drawScriptField(bool disable = true) {
218 var scriptProp = serializedObject.FindProperty("m_Script");
219 EditorGUI.BeginDisabledGroup(disable);
220 EditorGUILayout.PropertyField(scriptProp);
221 EditorGUI.EndDisabledGroup();
222 }
223
224 protected virtual void OnEnable() {
225 try {
226 if (serializedObject == null) { }
227 } catch (NullReferenceException) {
228 DestroyImmediate(this);
229 throw new Exception("Cleaning up an editor of type " + GetType() + ". Make sure to always destroy your editors when you are done with them!");
230 }
231
232 _specifiedDrawers = new Dictionary<string, Action<SerializedProperty>>();
233 _specifiedDecorators = new Dictionary<string, List<Action<SerializedProperty>>>();
234 _specifiedPostDecorators = new Dictionary<string, List<Action<SerializedProperty>>>();
235 _conditionalProperties = new Dictionary<string, List<Func<bool>>>();
236 _foldoutProperties = new Dictionary<string, List<string>>();
237 _foldoutStates = new Dictionary<string, bool>();
238 _deferredProperties = new List<string>();
239 _canCallSpecifyFunctions = true;
240 }
241
242 protected bool validateProperty(string propertyName) {
243 if (serializedObject.FindProperty(propertyName) == null) {
244 Debug.LogWarning("Property " + propertyName + " does not exist, was it removed or renamed?");
245 return false;
246 }
247 return true;
248 }
249
250 /*
251 * This method draws all visible properties, mirroring the default behavior of OnInspectorGUI.
252 * Individual properties can be specified to have custom drawers.
253 */
254 public override void OnInspectorGUI() {
255 // OnInspectorGUI is the first time "EditorStyles" can be accessed
256 if (_boldFoldoutStyle == null) {
257 _boldFoldoutStyle = new GUIStyle(EditorStyles.foldout);
258 _boldFoldoutStyle.fontStyle = FontStyle.Bold;
259 }
260
261 _canCallSpecifyFunctions = false;
262
263 _modifiedProperties.Clear();
264 SerializedProperty iterator = serializedObject.GetIterator();
265 bool isFirst = true;
266
267 while (iterator.NextVisible(isFirst)) {
268 if (isFirst && !_showScriptField) {
269 isFirst = false;
270 continue;
271 }
272
273 if (_deferredProperties.Contains(iterator.name) ||
274 isInFoldout(iterator.name)) {
275 continue;
276 }
277
278 using (new EditorGUI.DisabledGroupScope(isFirst)) {
279 drawProperty(iterator);
280 }
281
282 isFirst = false;
283 }
284
285 foreach (var deferredProperty in _deferredProperties) {
286 if (!isInFoldout(deferredProperty)) {
287 drawProperty(serializedObject.FindProperty(deferredProperty));
288 }
289 }
290
291 foreach (var foldout in _foldoutProperties) {
292 _foldoutStates[foldout.Key] =
293 EditorGUILayout.Foldout(_foldoutStates[foldout.Key], foldout.Key, _boldFoldoutStyle);
294 if (_foldoutStates[foldout.Key]) {
295 // Draw normal priority properties first
296 foreach (var property in foldout.Value) {
297 if (!_deferredProperties.Contains(property)) {
298 drawProperty(serializedObject.FindProperty(property));
299 }
300 }
301 // Draw deferred properties second
302 foreach (var property in foldout.Value) {
303 if (_deferredProperties.Contains(property)) {
304 drawProperty(serializedObject.FindProperty(property));
305 }
306 }
307 }
308 }
309
310 serializedObject.ApplyModifiedProperties();
311 }
312
313 private void drawProperty(SerializedProperty property) {
314 List<Func<bool>> conditionalList;
315 if (_conditionalProperties.TryGetValue(property.name, out conditionalList)) {
316 bool allTrue = true;
317 for (int i = 0; i < conditionalList.Count; i++) {
318 allTrue &= conditionalList[i]();
319 }
320 if (!allTrue) {
321 return;
322 }
323 }
324
325 Action<SerializedProperty> customDrawer;
326
327 List<Action<SerializedProperty>> decoratorList;
328 if (_specifiedDecorators.TryGetValue(property.name, out decoratorList)) {
329 for (int i = 0; i < decoratorList.Count; i++) {
330 decoratorList[i](property);
331 }
332 }
333
334 EditorGUI.BeginChangeCheck();
335
336 if (_specifiedDrawers.TryGetValue(property.name, out customDrawer)) {
337 customDrawer(property);
338 } else {
339 EditorGUILayout.PropertyField(property, true);
340 }
341
342 if (EditorGUI.EndChangeCheck()) {
343 _modifiedProperties.Add(property.Copy());
344 }
345
346
347 List<Action<SerializedProperty>> postDecoratorList;
348 if (_specifiedPostDecorators.TryGetValue(property.name, out postDecoratorList)) {
349 for (int i = 0; i < postDecoratorList.Count; i++) {
350 postDecoratorList[i](property);
351 }
352 }
353 }
354
355 private void throwIfNotInOnEnable(string methodName) {
356 if (!_canCallSpecifyFunctions) {
357 throw new InvalidOperationException("Cannot call " + methodName + " from within any other function but OnEnable. Make sure you also call base.OnEnable as well!");
358 }
359 }
360 }
361}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
void specifyConditionalDrawing(string conditionalName, params string[] dependantProperties)
Specify a list of properties that should only be displayed if the conditional property has a value of...
bool isInFoldout(string propertyName)
Check whether a property is inside of a foldout drop-down.
bool validateProperty(string propertyName)
void deferProperty(string propertyName)
Defer rendering of a property until the end of the inspector. Deferred properties are drawn in the RE...
List< string > _deferredProperties
void specifyConditionalDrawing(Func< bool > conditional, params string[] dependantProperties)
Dictionary< string, bool > _foldoutStates
void hideField(string propertyName)
Dictionary< string, List< Action< SerializedProperty > > > _specifiedDecorators
void drawScriptField(bool disable=true)
Dictionary< string, List< Action< SerializedProperty > > > _specifiedPostDecorators
void addPropertyToFoldout(string propertyName, string foldoutName, bool foldoutStartOpen=false)
Condition the drawing of a property based on the status of a foldout drop-down.
void specifyCustomPostDecorator(string propertyName, Action< SerializedProperty > decoratorDrawer)
Specify a callback to be used to draw a decorator AFTER a specific named property.
void specifyCustomDrawer(string propertyName, Action< SerializedProperty > propertyDrawer)
Specify a callback to be used to draw a specific named property. Should be called in OnEnable.
Dictionary< string, List< Func< bool > > > _conditionalProperties
Dictionary< string, Action< SerializedProperty > > _specifiedDrawers
List< SerializedProperty > _modifiedProperties
void specifyCustomDecorator(string propertyName, Action< SerializedProperty > decoratorDrawer)
Specify a callback to be used to draw a decorator for a specific named property. Should be called in ...
Dictionary< string, List< string > > _foldoutProperties
void specifyConditionalDrawing(string enumName, int enumValue, params string[] dependantProperties)
UnityEngine.Object Object