-
Notifications
You must be signed in to change notification settings - Fork 871
Expand file tree
/
Copy pathEditorUtilities.cs
More file actions
444 lines (374 loc) · 15.1 KB
/
EditorUtilities.cs
File metadata and controls
444 lines (374 loc) · 15.1 KB
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Rendering;
using UnityEngine.Rendering.PostProcessing;
#if XR_MANAGEMENT_4_0_1_OR_NEWER
using UnityEditor.XR.Management;
#endif
namespace UnityEditor.Rendering.PostProcessing
{
/// <summary>
/// A set of editor utilities used in post-processing editors.
/// </summary>
public static class EditorUtilities
{
static Dictionary<string, GUIContent> s_GUIContentCache;
static Dictionary<Type, AttributeDecorator> s_AttributeDecorators;
static PostProcessEffectSettings s_ClipboardContent;
/// <summary>
/// Returns <c>true</c> if the current target is a console, <c>false</c> otherwise.
/// </summary>
public static bool isTargetingConsoles
{
get
{
var t = EditorUserBuildSettings.activeBuildTarget;
return t == BuildTarget.PS4
#if UNITY_PS5
|| t == BuildTarget.PS5
#endif
|| t == BuildTarget.XboxOne
#if UNITY_GAMECORE
|| t == BuildTarget.GameCoreXboxSeries
|| t == BuildTarget.GameCoreXboxOne
#endif
#if UNITY_SWITCH2
|| t == BuildTarget.Switch2
#endif
|| t == BuildTarget.Switch;
}
}
/// <summary>
/// Returns <c>true</c> if the current target is a mobile, <c>false</c> otherwise.
/// </summary>
public static bool isTargetingMobiles
{
get
{
var t = EditorUserBuildSettings.activeBuildTarget;
return t == BuildTarget.Android
|| t == BuildTarget.iOS
|| t == BuildTarget.tvOS
#if !UNITY_2018_2_OR_NEWER
|| t == BuildTarget.Tizen
#endif
#if !UNITY_2018_3_OR_NEWER
|| t == BuildTarget.N3DS
|| t == BuildTarget.PSP2
#endif
;
}
}
/// <summary>
/// Returns <c>true</c> if the current target is Android, <c>false</c> otherwise.
/// </summary>
public static bool isTargetingAndroid
{
get
{
var t = EditorUserBuildSettings.activeBuildTarget;
return t == BuildTarget.Android;
}
}
/// <summary>
/// Returns <c>true</c> if the current build targets OpenGLES, <c>false</c> otherwise.
/// </summary>
public static bool isTargetingOpenGLES
{
get
{
var buildTargetAPIs = PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget);
foreach (var api in buildTargetAPIs)
{
if (api == GraphicsDeviceType.OpenGLES3
#if !UNITY_2023_1_OR_NEWER
|| api == GraphicsDeviceType.OpenGLES2
#endif
)
{
return true;
}
}
return false;
}
}
/// <summary>
/// Returns <c>true</c> if the current target is WebGL, <c>false</c> otherwise.
/// </summary>
public static bool isTargetingWebGL
{
get
{
var t = EditorUserBuildSettings.activeBuildTarget;
return t == BuildTarget.WebGL;
}
}
/// <summary>
/// Returns <c>true</c> if the current target is a console or a mobile, <c>false</c>
/// otherwise.
/// </summary>
public static bool isTargetingConsolesOrMobiles
{
get { return isTargetingConsoles || isTargetingMobiles; }
}
static EditorUtilities()
{
s_GUIContentCache = new Dictionary<string, GUIContent>();
s_AttributeDecorators = new Dictionary<Type, AttributeDecorator>();
ReloadDecoratorTypes();
}
[Callbacks.DidReloadScripts]
static void OnEditorReload()
{
ReloadDecoratorTypes();
}
static void ReloadDecoratorTypes()
{
s_AttributeDecorators.Clear();
// Look for all the valid attribute decorators
var types = RuntimeUtilities.GetAllTypesDerivedFrom<AttributeDecorator>()
.Where(
t => t.IsDefined(typeof(DecoratorAttribute), false)
&& !t.IsAbstract
);
// Store them
foreach (var type in types)
{
var attr = type.GetAttribute<DecoratorAttribute>();
var decorator = (AttributeDecorator)Activator.CreateInstance(type);
s_AttributeDecorators.Add(attr.attributeType, decorator);
}
}
internal static AttributeDecorator GetDecorator(Type attributeType)
{
AttributeDecorator decorator;
return !s_AttributeDecorators.TryGetValue(attributeType, out decorator)
? null
: decorator;
}
/// <summary>
/// Gets a <see cref="GUIContent"/> for the given label and tooltip. These are recycled
/// internally and help reduce the garbage collector pressure in the editor.
/// </summary>
/// <param name="textAndTooltip">The label and tooltip separated by a <c>|</c>
/// character</param>
/// <returns>A recycled <see cref="GUIContent"/></returns>
public static GUIContent GetContent(string textAndTooltip)
{
if (string.IsNullOrEmpty(textAndTooltip))
return GUIContent.none;
GUIContent content;
if (!s_GUIContentCache.TryGetValue(textAndTooltip, out content))
{
var s = textAndTooltip.Split('|');
content = new GUIContent(s[0]);
if (s.Length > 1 && !string.IsNullOrEmpty(s[1]))
content.tooltip = s[1];
s_GUIContentCache.Add(textAndTooltip, content);
}
return content;
}
/// <summary>
/// Draws a UI box with a description and a "Fix Me" button next to it.
/// </summary>
/// <param name="text">The description</param>
/// <param name="action">The action to execute when the button is clicked</param>
public static void DrawFixMeBox(string text, Action action)
{
Assert.IsNotNull(action);
EditorGUILayout.HelpBox(text, MessageType.Warning);
GUILayout.Space(-32);
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (GUILayout.Button("Fix", GUILayout.Width(60)))
action();
GUILayout.Space(8);
}
GUILayout.Space(11);
}
/// <summary>
/// Draws a horizontal split line.
/// </summary>
public static void DrawSplitter()
{
var rect = GUILayoutUtility.GetRect(1f, 1f);
// Splitter rect should be full-width
rect.xMin = 0f;
rect.width += 4f;
if (Event.current.type != EventType.Repaint)
return;
EditorGUI.DrawRect(rect, Styling.splitter);
}
/// <summary>
/// Draws a toggle using the "override checkbox" style.
/// </summary>
/// <param name="rect">The position and size of the toggle</param>
/// <param name="property">The override state property for the toggle</param>
public static void DrawOverrideCheckbox(Rect rect, SerializedProperty property)
{
property.boolValue = GUI.Toggle(rect, property.boolValue, GetContent("|Override this setting for this volume."), Styling.smallTickbox);
}
/// <summary>
/// Draws a header label.
/// </summary>
/// <param name="title">The label to display as a header</param>
public static void DrawHeaderLabel(string title)
{
EditorGUILayout.LabelField(title, Styling.headerLabel);
}
internal static bool DrawHeader(string title, bool state)
{
var backgroundRect = GUILayoutUtility.GetRect(1f, 17f);
var labelRect = backgroundRect;
labelRect.xMin += 16f;
labelRect.xMax -= 20f;
var foldoutRect = backgroundRect;
foldoutRect.y += 1f;
foldoutRect.width = 13f;
foldoutRect.height = 13f;
// Background rect should be full-width
backgroundRect.xMin = 0f;
backgroundRect.width += 4f;
// Background
EditorGUI.DrawRect(backgroundRect, Styling.headerBackground);
// Title
EditorGUI.LabelField(labelRect, GetContent(title), EditorStyles.boldLabel);
// Foldout
state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout);
var e = Event.current;
if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && e.button == 0)
{
state = !state;
e.Use();
}
return state;
}
internal static bool DrawHeader(string title, SerializedProperty group, SerializedProperty activeField, PostProcessEffectSettings target, Action resetAction, Action removeAction)
{
Assert.IsNotNull(group);
Assert.IsNotNull(activeField);
Assert.IsNotNull(target);
var backgroundRect = GUILayoutUtility.GetRect(1f, 17f);
var labelRect = backgroundRect;
labelRect.xMin += 32f;
labelRect.xMax -= 20f;
var foldoutRect = backgroundRect;
foldoutRect.y += 1f;
foldoutRect.width = 13f;
foldoutRect.height = 13f;
var toggleRect = backgroundRect;
toggleRect.x += 16f;
toggleRect.y += 2f;
toggleRect.width = 13f;
toggleRect.height = 13f;
var menuIcon = Styling.paneOptionsIcon;
#if UNITY_2019_3_OR_NEWER
var menuRect = new Rect(labelRect.xMax + 4f, labelRect.y, menuIcon.width, menuIcon.height);
#else
var menuRect = new Rect(labelRect.xMax + 4f, labelRect.y + 4f, menuIcon.width, menuIcon.height);
#endif
// Background rect should be full-width
backgroundRect.xMin = 0f;
backgroundRect.width += 4f;
// Background
EditorGUI.DrawRect(backgroundRect, Styling.headerBackground);
// Title
using (new EditorGUI.DisabledScope(!activeField.boolValue))
EditorGUI.LabelField(labelRect, GetContent(title), EditorStyles.boldLabel);
// foldout
group.serializedObject.Update();
group.isExpanded = GUI.Toggle(foldoutRect, group.isExpanded, GUIContent.none, EditorStyles.foldout);
group.serializedObject.ApplyModifiedProperties();
// Active checkbox
activeField.serializedObject.Update();
activeField.boolValue = GUI.Toggle(toggleRect, activeField.boolValue, GUIContent.none, Styling.smallTickbox);
activeField.serializedObject.ApplyModifiedProperties();
// Dropdown menu icon
GUI.DrawTexture(menuRect, menuIcon);
// Handle events
var e = Event.current;
if (e.type == EventType.MouseDown)
{
if (menuRect.Contains(e.mousePosition))
{
ShowHeaderContextMenu(new Vector2(menuRect.x, menuRect.yMax), target, resetAction, removeAction);
e.Use();
}
else if (labelRect.Contains(e.mousePosition))
{
if (e.button == 0)
group.isExpanded = !group.isExpanded;
else
ShowHeaderContextMenu(e.mousePosition, target, resetAction, removeAction);
e.Use();
}
}
return group.isExpanded;
}
static void ShowHeaderContextMenu(Vector2 position, PostProcessEffectSettings target, Action resetAction, Action removeAction)
{
Assert.IsNotNull(resetAction);
Assert.IsNotNull(removeAction);
var menu = new GenericMenu();
menu.AddItem(GetContent("Reset"), false, () => resetAction());
menu.AddItem(GetContent("Remove"), false, () => removeAction());
menu.AddSeparator(string.Empty);
menu.AddItem(GetContent("Copy Settings"), false, () => CopySettings(target));
if (CanPaste(target))
menu.AddItem(GetContent("Paste Settings"), false, () => PasteSettings(target));
else
menu.AddDisabledItem(GetContent("Paste Settings"));
menu.DropDown(new Rect(position, Vector2.zero));
}
static void CopySettings(PostProcessEffectSettings target)
{
Assert.IsNotNull(target);
if (s_ClipboardContent != null)
{
RuntimeUtilities.Destroy(s_ClipboardContent);
s_ClipboardContent = null;
}
s_ClipboardContent = (PostProcessEffectSettings)ScriptableObject.CreateInstance(target.GetType());
EditorUtility.CopySerializedIfDifferent(target, s_ClipboardContent);
}
static void PasteSettings(PostProcessEffectSettings target)
{
Assert.IsNotNull(target);
Assert.IsNotNull(s_ClipboardContent);
Assert.AreEqual(s_ClipboardContent.GetType(), target.GetType());
Undo.RecordObject(target, "Paste Settings");
EditorUtility.CopySerializedIfDifferent(s_ClipboardContent, target);
}
static bool CanPaste(PostProcessEffectSettings target)
{
return s_ClipboardContent != null
&& s_ClipboardContent.GetType() == target.GetType();
}
internal static bool isVREnabled
{
get
{
#if ENABLE_VR_MODULE && ENABLE_VR
#if ENABLE_XR_MODULE && XR_MANAGEMENT_4_0_1_OR_NEWER
// If XR manager extension is available, we can query it to know if any XR extension is currently active
var buildTargetSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTargetGroup.Standalone);
return (buildTargetSettings != null && buildTargetSettings.AssignedSettings != null &&
buildTargetSettings.AssignedSettings.activeLoaders.Count > 0);
#elif !UNITY_2020_1_OR_NEWER
// This will only work with 2019.3 and older since it rely on the old VR module
return UnityEditorInternal.VR.VREditor.GetVREnabledOnTargetGroup(BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget));
#else
// If we reach this code-path, it means we can't really detect if VR/XR is active in the Editor, so return false
return false;
#endif
#else
return false;
#endif
}
}
}
}