mixedreality/com.microsoft.mixedreality..../Core/Inspectors/PropertyDrawers/SceneInfoDrawer.cs

269 lines
12 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.SceneSystem;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Editor
{
/// <summary>
/// Draws the scene info struct and populates its hidden fields.
/// </summary>
[CustomPropertyDrawer(typeof(SceneInfo))]
public class SceneInfoDrawer : PropertyDrawer
{
/// <summary>
/// Used to control whether to draw the tag property.
/// All scenes can have tags, but they're not always relevant based on how the scene is being used.
/// Not sure how much I like this method of controlling property drawing since it could result in unpredictable behavior in inspectors.
/// We could add an enum or bool to the SceneInfo struct to control this, but that seemed like unnecessary clutter.
/// </summary>
public static bool DrawTagProperty { get; set; }
const float iconWidth = 20f;
const float totalPropertyWidth = 410;
const float assetPropertyWidth = 400;
const float tagPropertyWidth = 400;
const float buttonPropertyWidth = 400;
const float assetLabelWidth = 150;
const float tagLabelWidth = 40;
const string enabledIconContent = "TestPassed";
const string missingIconContent = "TestIgnored";
const string disabledIconContent = "TestNormal";
const string warningIconContent = "TestInconclusive";
const string errorIconContent = "TestFailed";
static RectOffset boxOffset;
static GUIStyle italicStyle;
public static float GetPropertyHeight(bool drawTagProperty)
{
return (EditorGUIUtility.standardVerticalSpacing + EditorGUIUtility.singleLineHeight) * (drawTagProperty ? 4 : 3);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return GetPropertyHeight(DrawTagProperty);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
DrawProperty(position, property, label);
}
public static void DrawProperty(Rect position, SerializedProperty property, GUIContent label, bool isActive = false, bool isSelected = false)
{
SerializedProperty assetProperty, nameProperty, pathProperty, buildIndexProperty, includedProperty, tagProperty;
SceneInfoUtils.GetSceneInfoRelativeProperties(property, out assetProperty, out nameProperty, out pathProperty, out buildIndexProperty, out includedProperty, out tagProperty);
// Set up our properties and settings
boxOffset = EditorStyles.helpBox.padding;
if (italicStyle == null) { italicStyle = new GUIStyle(EditorStyles.label); }
bool lastMode = EditorGUIUtility.wideMode;
int lastIndentLevel = EditorGUI.indentLevel;
EditorGUIUtility.wideMode = true;
EditorGUI.BeginProperty(position, label, property);
GUI.color = isActive ? Color.gray : GUI.backgroundColor;
GUI.color = isSelected ? Color.blue : GUI.backgroundColor;
// Indent our rect, then reset indent to 0 so sub-properties don't get doubly indented
position = EditorGUI.IndentedRect(position);
EditorGUI.indentLevel = 0;
// Draw a box around our item
Rect boxPosition = position;
boxPosition.height = (EditorGUIUtility.singleLineHeight * (DrawTagProperty ? 4 : 3)) - EditorGUIUtility.standardVerticalSpacing;
GUI.Box(boxPosition, GUIContent.none, EditorStyles.helpBox);
position = boxOffset.Remove(position);
Rect iconRect = position;
iconRect.width = iconWidth;
Rect assetRect = position;
assetRect.width = position.width - iconWidth;
assetRect.height = EditorGUIUtility.singleLineHeight;
assetRect.x += iconWidth;
Rect buttonRect = position;
buttonRect.height = EditorGUIUtility.singleLineHeight;
buttonRect.y += (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing);
Rect tagRect = position;
tagRect.height = EditorGUIUtility.singleLineHeight;
tagRect.y += ((EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * 2) + EditorGUIUtility.standardVerticalSpacing;
bool changed = false;
UnityEngine.Object asset = assetProperty.objectReferenceValue;
if (!Application.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isCompiling)
{ // This is expensive so don't refresh during play mode or while other stuff is going on
changed = SceneInfoUtils.RefreshSceneInfo(asset, nameProperty, pathProperty, buildIndexProperty, includedProperty, tagProperty);
}
GUIContent labelContent = null;
GUIContent iconContent = null;
italicStyle.fontStyle = FontStyle.Normal;
bool buttonsDisabled = false;
bool tagDisabled = false;
if (asset == null)
{
string missingSceneName = nameProperty.stringValue;
if (!string.IsNullOrEmpty(missingSceneName))
{
labelContent = new GUIContent(" (" + missingSceneName + ")");
labelContent.tooltip = "The scene " + missingSceneName + " is missing. It will not be available to load.";
italicStyle.fontStyle = FontStyle.Italic;
tagDisabled = true;
iconContent = EditorGUIUtility.IconContent(missingIconContent);
}
else
{
labelContent = new GUIContent(" (Empty)");
labelContent.tooltip = "This scene is empty. You should assign a scene object before building.";
buttonsDisabled = true;
tagDisabled = true;
iconContent = EditorGUIUtility.IconContent(missingIconContent);
}
}
else
{
if (includedProperty.boolValue)
{
if (buildIndexProperty.intValue >= 0)
{
labelContent = new GUIContent(" Build index: " + buildIndexProperty.intValue);
labelContent.tooltip = "This scene is in build settings at index " + buildIndexProperty.intValue;
iconContent = EditorGUIUtility.IconContent(enabledIconContent);
}
else
{
labelContent = new GUIContent(" (Disabled)");
labelContent.tooltip = "This scene is in build settings at index " + buildIndexProperty.intValue + ", but it has been disabled and will not be available to load.";
iconContent = EditorGUIUtility.IconContent(disabledIconContent);
}
}
else
{
labelContent = new GUIContent(" (Not included in build)");
labelContent.tooltip = "This scene is not included in build settings and will not be available to load.";
iconContent = EditorGUIUtility.IconContent(errorIconContent);
}
}
// Draw our icon
EditorGUI.LabelField(iconRect, iconContent);
// Draw our object field
EditorGUI.BeginDisabledGroup(Application.isPlaying);
EditorGUIUtility.labelWidth = assetLabelWidth;
asset = EditorGUI.ObjectField(assetRect, labelContent, assetProperty.objectReferenceValue, typeof(SceneAsset), false);
EditorGUI.EndDisabledGroup();
if (DrawTagProperty)
{
// Draw our tag field
EditorGUI.BeginDisabledGroup(tagDisabled || Application.isPlaying);
EditorGUIUtility.labelWidth = tagLabelWidth;
changed |= EditorGUI.PropertyField(tagRect, tagProperty);
EditorGUI.EndDisabledGroup();
}
// Draw our button
EditorGUI.BeginDisabledGroup(buttonsDisabled || Application.isPlaying);
if (!string.IsNullOrEmpty(pathProperty.stringValue) && asset == null)
{
// The scene is missing
// This may be due to a local file ID mismatch
// Try to find it based on guid first
asset = AssetDatabase.LoadAssetAtPath<SceneAsset>(pathProperty.stringValue);
}
if (!string.IsNullOrEmpty(nameProperty.stringValue) && asset == null)
{
// If we still can't find it, draw a button that lets people attempt to recover it
if (GUI.Button(buttonRect, "Search for missing scene", EditorStyles.toolbarButton))
{
changed |= SceneInfoUtils.FindScene(nameProperty, pathProperty, ref asset);
}
}
else
{
// It's not included in build settings
if (!includedProperty.boolValue)
{
// The scene exists but it isn't in our build settings
// Show a button that lets us add it
if (GUI.Button(buttonRect, "Add to build settings", EditorStyles.toolbarButton) && asset != null)
{
List<EditorBuildSettingsScene> scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
scenes.Add(new EditorBuildSettingsScene(pathProperty.stringValue, true));
includedProperty.boolValue = true;
EditorBuildSettings.scenes = scenes.ToArray();
SceneInfoUtils.RefreshCachedScenes();
changed = true;
}
}
else
{
bool enabledInBuild = buildIndexProperty.intValue >= 0;
// The scene exists and is in build settings
// Show enable / disable toggle
if (GUI.Button(buttonRect, enabledInBuild ? "Disable in build settings" : "Enable in build settings", EditorStyles.toolbarButton))
{
enabledInBuild = !enabledInBuild;
// Modify a local copy of our scenes instead of using the cached scenes
EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
// Find the scene in our build settings and enable / disable it
int sceneCount = 0;
int buildIndex = -1;
for (int i = 0; i < SceneInfoUtils.CachedScenes.Length; i++)
{
if (scenes[i].path == pathProperty.stringValue)
{
scenes[i].enabled = enabledInBuild;
if (scenes[i].enabled)
{ // Only store the build index if it's enabled
buildIndex = sceneCount;
}
break;
}
if (scenes[i].enabled)
{ // Disabled scenes don't count toward scene count
sceneCount++;
}
}
EditorBuildSettings.scenes = scenes;
SceneInfoUtils.RefreshCachedScenes();
buildIndexProperty.intValue = buildIndex;
changed = true;
}
}
}
EditorGUI.EndDisabledGroup();
if (asset != assetProperty.objectReferenceValue)
{
assetProperty.objectReferenceValue = asset;
changed = true;
}
if (changed)
{
property.serializedObject.ApplyModifiedProperties();
}
EditorGUIUtility.wideMode = lastMode;
EditorGUI.indentLevel = lastIndentLevel;
EditorGUI.EndProperty();
}
}
}