Tanoda
LeapGraphicGroupEditor.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 System.IO;
10using System.Reflection;
11using System.Collections.Generic;
12using UnityEngine;
13using UnityEngine.Assertions;
14using UnityEditor;
15using UnityEditorInternal;
16using Leap.Unity;
17using Leap.Unity.Query;
18
20
21 public class LeapGuiGroupEditor {
22 public const int BUTTON_WIDTH = 30;
23 public const int REFRESH_WIDTH = 78;
24
25 private LeapGraphicRenderer _renderer;
26 private SerializedObject _serializedObject;
27
28 private SerializedProperty _supportInfo;
29
30 private SerializedProperty _groupProperty;
31
32 private SerializedProperty _multiFeatureList;
33 private SerializedProperty _multiRenderingMethod;
34 private SerializedProperty _featureTable;
35 private ReorderableList _featureList;
36 private MonoScript _renderingMethodMonoScript;
37
38 private List<SerializedProperty> _cachedPropertyList;
39 private List<float> _cachedPropertyHeights;
40
41 private SerializedProperty _renderingMethod;
42
43 private GenericMenu _addRenderingMethodMenu;
44 private GenericMenu _addFeatureMenu;
45
46 public LeapGuiGroupEditor(LeapGraphicRenderer renderer, SerializedObject serializedObject) {
47 _renderer = renderer;
48 _serializedObject = serializedObject;
49
50 var allTypes = Assembly.GetAssembly(typeof(LeapGraphicRenderer)).GetTypes();
51 var allRenderingMethods = allTypes.Query().
52 Where(t => !t.IsAbstract &&
53 !t.IsGenericType &&
54 t.IsSubclassOf(typeof(LeapRenderingMethod)));
55
56 _addRenderingMethodMenu = new GenericMenu();
57 foreach (var renderingMethod in allRenderingMethods) {
58 _addRenderingMethodMenu.AddItem(new GUIContent(LeapGraphicTagAttribute.GetTagName(renderingMethod)),
59 false,
60 () => {
61 serializedObject.ApplyModifiedProperties();
62 Undo.RecordObject(_renderer, "Changed rendering method");
63 EditorUtility.SetDirty(_renderer);
64 _renderer.editor.ChangeRenderingMethodOfSelectedGroup(renderingMethod, addFeatures: false);
65 serializedObject.Update();
66 _renderer.editor.ScheduleRebuild();
67 _serializedObject.SetIsDifferentCacheDirty();
68 });
69 }
70
71 var allFeatures = allTypes.Query().
72 Where(t => !t.IsAbstract &&
73 !t.IsGenericType &&
74 t.IsSubclassOf(typeof(LeapGraphicFeatureBase))).ToList();
75
76 allFeatures.Sort((a, b) => {
77 var tagA = LeapGraphicTagAttribute.GetTag(a);
78 var tagB = LeapGraphicTagAttribute.GetTag(b);
79 var orderA = tagA == null ? 0 : tagA.order;
80 var orderB = tagB == null ? 0 : tagB.order;
81 return orderA - orderB;
82 });
83
84 _addFeatureMenu = new GenericMenu();
85 foreach (var item in allFeatures.Query().WithPrevious(includeStart: true)) {
86 var tag = LeapGraphicTagAttribute.GetTag(item.value);
87 var order = tag == null ? 0 : tag.order;
88
89 if (item.hasPrev) {
90 var prevTag = LeapGraphicTagAttribute.GetTag(item.prev);
91 var prevOrder = prevTag == null ? 0 : prevTag.order;
92 if ((prevOrder / 100) != (order / 100)) {
93 _addFeatureMenu.AddSeparator("");
94 }
95 }
96
97 _addFeatureMenu.AddItem(new GUIContent(tag.name),
98 false,
99 () => {
100 if (item.value.ImplementsInterface(typeof(ICustomChannelFeature)) && LeapGraphicPreferences.promptWhenAddCustomChannel) {
101 int result = EditorUtility.DisplayDialogComplex("Adding Custom Channel", "Custom channels can only be utilized by writing custom shaders, are you sure you want to continue?", "Add it", "Cancel", "Add it from now on");
102 switch (result) {
103 case 0:
104 break;
105 case 1:
106 return;
107 case 2:
108 LeapGraphicPreferences.promptWhenAddCustomChannel = false;
109 break;
110 }
111 }
112
113 serializedObject.ApplyModifiedProperties();
114 Undo.RecordObject(_renderer, "Added feature");
115 EditorUtility.SetDirty(_renderer);
116 _renderer.editor.AddFeatureToSelectedGroup(item.value);
117 _serializedObject.Update();
118 _serializedObject.SetIsDifferentCacheDirty();
119 });
120 }
121 }
122
123 public void Invalidate() {
124 _featureList = null;
125 _renderingMethodMonoScript = null;
126 }
127
128 public void DoGuiLayout(SerializedProperty groupProperty) {
129 using (new ProfilerSample("Draw Graphic Group")) {
130 init(groupProperty);
131
132 drawRendererHeader();
133
134 drawGroupName();
135
136 drawMonoScript();
137
138 drawStatsArea();
139
140 drawSpriteWarning();
141
142 EditorGUILayout.PropertyField(_renderingMethod, includeChildren: true);
143
144 EditorGUILayout.Space();
145
146 drawFeatureHeader();
147
148 _featureList.DoLayoutList();
149
150 drawWarningDialogs();
151 }
152 }
153
154 private void init(SerializedProperty groupProperty) {
155 Assert.IsNotNull(groupProperty);
156 _groupProperty = groupProperty;
157
158 _multiFeatureList = _groupProperty.FindPropertyRelative("_features");
159 _multiRenderingMethod = _groupProperty.FindPropertyRelative("_renderingMethod");
160
161 _featureTable = MultiTypedListUtil.GetTableProperty(_multiFeatureList);
162 Assert.IsNotNull(_featureTable);
163
164 if (_featureList == null || !SerializedProperty.EqualContents(_featureList.serializedProperty, _featureTable)) {
165 _featureList = new ReorderableList(_serializedObject,
166 _featureTable,
167 draggable: true,
168 displayHeader: false,
169 displayAddButton: false,
170 displayRemoveButton: false);
171
172 _featureList.showDefaultBackground = false;
173 _featureList.headerHeight = 0;
174 _featureList.elementHeight = EditorGUIUtility.singleLineHeight;
175 _featureList.elementHeightCallback = featureHeightCallback;
176 _featureList.drawElementCallback = drawFeatureCallback;
177 _featureList.onReorderCallback = onReorderFeaturesCallback;
178 }
179
180 _renderingMethod = MultiTypedReferenceUtil.GetReferenceProperty(_multiRenderingMethod);
181 _supportInfo = _groupProperty.FindPropertyRelative("_supportInfo");
182
183 _cachedPropertyList = new List<SerializedProperty>();
184 _cachedPropertyHeights = new List<float>();
185
186 for (int i = 0; i < _featureTable.arraySize; i++) {
187 var idIndex = _featureTable.GetArrayElementAtIndex(i);
188 var referenceProp = MultiTypedListUtil.GetReferenceProperty(_multiFeatureList, idIndex);
189 _cachedPropertyList.Add(referenceProp);
190
191 //Make sure to add one line for the label
192 _cachedPropertyHeights.Add(EditorGUI.GetPropertyHeight(referenceProp) + EditorGUIUtility.singleLineHeight);
193 }
194
195 _renderingMethod = MultiTypedReferenceUtil.GetReferenceProperty(_multiRenderingMethod);
196
197 if (_renderingMethodMonoScript == null) {
198 _renderingMethodMonoScript = AssetDatabase.FindAssets(_renderingMethod.type).
199 Query().
200 Where(guid => !string.IsNullOrEmpty(guid)).
201 Select(guid => AssetDatabase.GUIDToAssetPath(guid)).
202 Where(path => Path.GetFileNameWithoutExtension(path) == _renderingMethod.type).
203 Select(path => AssetDatabase.LoadAssetAtPath<MonoScript>(path)).
204 FirstOrDefault();
205 }
206 }
207
208 private void drawGroupName() {
209 var nameProperty = _groupProperty.FindPropertyRelative("_groupName");
210 EditorGUILayout.PropertyField(nameProperty);
211
212 nameProperty.stringValue = nameProperty.stringValue.Trim();
213
214 if (string.IsNullOrEmpty(nameProperty.stringValue)) {
215 nameProperty.stringValue = "MyGroupName";
216 }
217 }
218
219 private void drawRendererHeader() {
220 Rect rect = EditorGUILayout.GetControlRect(GUILayout.MaxHeight(EditorGUIUtility.singleLineHeight));
221 Rect left, right;
222 rect.SplitHorizontallyWithRight(out left, out right, BUTTON_WIDTH * 2);
223
224 #if UNITY_2018_3_OR_NEWER
225 if (!EditorApplication.isPlaying &&
226 // Allow normal gameobjects & prefab _instances_ but not prefab assets.
227 !PrefabUtility.IsPartOfAnyPrefab(_renderer) ||
228 PrefabUtility.GetPrefabInstanceStatus(_renderer) != PrefabInstanceStatus.NotAPrefab)
229 {
230 #else
231 if (!EditorApplication.isPlaying &&
232 PrefabUtility.GetPrefabType(_renderer) != PrefabType.Prefab)
233 {
234 #endif
235 var mesher = _renderer.editor.GetSelectedRenderingMethod() as LeapMesherBase;
236 if (mesher != null) {
237
238 Color prevColor = GUI.color;
239 if (mesher.IsAtlasDirty) {
240 GUI.color = Color.yellow;
241 }
242
243 Rect middle;
244 left.SplitHorizontallyWithRight(out left, out middle, REFRESH_WIDTH);
245 if (GUI.Button(middle, "Refresh Atlas", EditorStyles.miniButtonMid)) {
246 _serializedObject.ApplyModifiedProperties();
247 Undo.RecordObject(_renderer, "Refreshed atlas");
248 EditorUtility.SetDirty(_renderer);
249 mesher.RebuildAtlas(new ProgressBar());
250 _renderer.editor.ScheduleRebuild();
251 _serializedObject.Update();
252 }
253
254 GUI.color = prevColor;
255 }
256 }
257
258 EditorGUI.LabelField(left, "Renderer", EditorStyles.miniButtonLeft);
259 using (new EditorGUI.DisabledGroupScope(EditorApplication.isPlaying)) {
260 if (GUI.Button(right, "v", EditorStyles.miniButtonRight)) {
261 _addRenderingMethodMenu.ShowAsContext();
262 }
263 }
264 }
265
266 private void drawStatsArea() {
267 using (new EditorGUI.DisabledGroupScope(true)) {
268 var graphicList = _groupProperty.FindPropertyRelative("_graphics");
269 int count = graphicList.arraySize;
270 EditorGUILayout.IntField("Attached Graphic Count", count);
271 }
272 }
273
274 private void drawSpriteWarning() {
275 var list = Pool<List<LeapGraphicFeatureBase>>.Spawn();
276
277 try {
278 foreach (var group in _renderer.groups) {
279 list.AddRange(group.features);
280 }
281 SpriteAtlasUtil.ShowInvalidSpriteWarning(list);
282 } finally {
283 list.Clear();
284 Pool<List<LeapGraphicFeatureBase>>.Recycle(list);
285 }
286 }
287
288 private void drawMonoScript() {
289 using (new EditorGUI.DisabledGroupScope(true)) {
290 EditorGUILayout.ObjectField("Rendering Method",
291 _renderingMethodMonoScript,
292 typeof(MonoScript),
293 allowSceneObjects: false);
294 }
295 }
296
297 private void drawWarningDialogs() {
298 HashSet<string> shownMessages = Pool<HashSet<string>>.Spawn();
299 try {
300 for (int i = 0; i < _cachedPropertyList.Count; i++) {
301 if (!EditorApplication.isPlaying) {
302 var supportInfo = _supportInfo.GetArrayElementAtIndex(i);
303 var supportProperty = supportInfo.FindPropertyRelative("support");
304 var messageProperty = supportInfo.FindPropertyRelative("message");
305
306 if (shownMessages.Contains(messageProperty.stringValue)) {
307 continue;
308 }
309 shownMessages.Add(messageProperty.stringValue);
310
311 switch ((SupportType)supportProperty.intValue) {
312 case SupportType.Warning:
313 EditorGUILayout.HelpBox(messageProperty.stringValue, MessageType.Warning);
314 break;
315 case SupportType.Error:
316 EditorGUILayout.HelpBox(messageProperty.stringValue, MessageType.Error);
317 break;
318 }
319 }
320 }
321 } finally {
322 shownMessages.Clear();
323 Pool<HashSet<string>>.Recycle(shownMessages);
324 }
325
326 }
327
328 private void drawFeatureHeader() {
329 Rect rect = EditorGUILayout.GetControlRect(GUILayout.MaxHeight(EditorGUIUtility.singleLineHeight));
330 Rect left, middle, right;
331 rect.SplitHorizontallyWithRight(out middle, out right, BUTTON_WIDTH);
332 middle.SplitHorizontallyWithRight(out left, out middle, BUTTON_WIDTH);
333
334 EditorGUI.LabelField(left, "Graphic Features", EditorStyles.miniButtonLeft);
335
336 using (new EditorGUI.DisabledGroupScope(EditorApplication.isPlaying)) {
337 EditorGUI.BeginDisabledGroup(_featureTable.arraySize == 0);
338 if (GUI.Button(middle, "-", EditorStyles.miniButtonMid) && _featureList.index >= 0) {
339 _serializedObject.ApplyModifiedProperties();
340 Undo.RecordObject(_renderer, "Removed Feature");
341 EditorUtility.SetDirty(_renderer);
342 _renderer.editor.RemoveFeatureFromSelectedGroup(_featureList.index);
343 _serializedObject.Update();
344 init(_groupProperty);
345 }
346 EditorGUI.EndDisabledGroup();
347
348 if (GUI.Button(right, "+", EditorStyles.miniButtonRight)) {
349 _addFeatureMenu.ShowAsContext();
350 }
351 }
352 }
353
354 // Feature list callbacks
355
356 private void drawFeatureHeaderCallback(Rect rect) {
357 Rect left, right;
358 rect.SplitHorizontallyWithRight(out left, out right, BUTTON_WIDTH);
359
360 EditorGUI.LabelField(left, "Graphic Features", EditorStyles.miniButtonLeft);
361
362 if (GUI.Button(right, "+", EditorStyles.miniButtonRight)) {
363 _addFeatureMenu.ShowAsContext();
364 }
365 }
366
367 private float featureHeightCallback(int index) {
368 return _cachedPropertyHeights[index];
369 }
370
371 delegate void Action<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5);
372
373 private void drawFeatureCallback(Rect rect, int index, bool isActive, bool isFocused) {
374 var featureProperty = _cachedPropertyList[index];
375
376 rect = rect.SingleLine();
377 string featureName = LeapGraphicTagAttribute.GetTagName(featureProperty.type);
378
379 int lastIndexOf = featureName.LastIndexOf('/');
380 if (lastIndexOf >= 0) {
381 featureName = featureName.Substring(lastIndexOf + 1);
382 }
383
384 GUIContent featureLabel = new GUIContent(featureName);
385
386 Color originalColor = GUI.color;
387
388 if (!EditorApplication.isPlaying &&
389 index < _supportInfo.arraySize) {
390 var supportInfo = _supportInfo.GetArrayElementAtIndex(index);
391 var supportProperty = supportInfo.FindPropertyRelative("support");
392 var messageProperty = supportInfo.FindPropertyRelative("message");
393 switch ((SupportType)supportProperty.intValue) {
394 case SupportType.Warning:
395 GUI.color = Color.yellow;
396 featureLabel.tooltip = messageProperty.stringValue;
397 break;
398 case SupportType.Error:
399 GUI.color = Color.red;
400 featureLabel.tooltip = messageProperty.stringValue;
401 break;
402 }
403 }
404
405 Vector2 size = EditorStyles.label.CalcSize(featureLabel);
406
407 Rect labelRect = rect;
408 labelRect.width = size.x;
409
410 GUI.Box(labelRect, "");
411 EditorGUI.LabelField(labelRect, featureLabel);
412 GUI.color = originalColor;
413
414
415 rect = rect.NextLine().Indent();
416 EditorGUI.PropertyField(rect, featureProperty, includeChildren: true);
417 }
418
419 private void onReorderFeaturesCallback(ReorderableList list) {
420 _renderer.editor.ScheduleRebuild();
421 }
422 }
423}
UnityEngine.Color Color
Definition: TestScript.cs:32
static LeapGraphicTagAttribute GetTag(Type type)
LeapGuiGroupEditor(LeapGraphicRenderer renderer, SerializedObject serializedObject)
void DoGuiLayout(SerializedProperty groupProperty)
This class allows you to easily give feedback of an action as it completes.
Definition: ProgressBar.cs:66
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....
A Query object is a type of immutable ordered collection of elements that can be used to perform usef...
Definition: Query.cs:90