namespace VRTK { using UnityEngine; using UnityEditor; using UnityEditorInternal; using System; using System.Collections.Generic; using System.Linq; [CustomEditor(typeof(VRTK_SDKManager))] public class VRTK_SDKManagerEditor : Editor { private static readonly Dictionary isBuildTargetActiveSymbolsFoldOut; private ReorderableList setupsList; static VRTK_SDKManagerEditor() { BuildTargetGroup[] targetGroups = VRTK_SharedMethods.GetValidBuildTargetGroups(); isBuildTargetActiveSymbolsFoldOut = new Dictionary(targetGroups.Length); foreach (BuildTargetGroup targetGroup in targetGroups) { isBuildTargetActiveSymbolsFoldOut[targetGroup] = true; } } protected virtual void OnEnable() { VRTK_SDKManager sdkManager = (VRTK_SDKManager)target; setupsList = new ReorderableList(serializedObject, serializedObject.FindProperty("setups")) { headerHeight = 2, drawElementCallback = (rect, index, active, focused) => { SerializedProperty serializedProperty = setupsList.serializedProperty; if (serializedProperty.arraySize <= index) { return; } rect.y += 2; rect.height = EditorGUIUtility.singleLineHeight; Color previousColor = GUI.color; if (IsSDKSetupNeverUsed(index)) { GUI.color = new Color(previousColor.r, previousColor.g, previousColor.b, 0.5f); } GUIContent unloadButtonGUIContent = new GUIContent("Unload", "Unload this SDK Setup."); GUIContent loadButtonGUIContent = new GUIContent("Load", "Try to load this SDK Setup."); float buttonGUIContentWidth = Mathf.Max( GUI.skin.button.CalcSize(unloadButtonGUIContent).x, GUI.skin.button.CalcSize(loadButtonGUIContent).x ); VRTK_SDKSetup setup = (VRTK_SDKSetup)serializedProperty.GetArrayElementAtIndex(index).objectReferenceValue; if (EditorApplication.isPlaying && setup != null) { rect.width -= buttonGUIContentWidth + ReorderableList.Defaults.padding; } EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(rect, serializedProperty.GetArrayElementAtIndex(index), GUIContent.none); if (EditorGUI.EndChangeCheck()) { setup = (VRTK_SDKSetup)serializedProperty.GetArrayElementAtIndex(index).objectReferenceValue; if (setup != null) { int indexOfExistingDuplicateSetup = Enumerable .Range(0, serializedProperty.arraySize) .Except(new[] { index }) .Where(i => (VRTK_SDKSetup)serializedProperty.GetArrayElementAtIndex(i).objectReferenceValue == setup) .DefaultIfEmpty(-1) .First(); if (indexOfExistingDuplicateSetup != -1) { serializedProperty.GetArrayElementAtIndex(indexOfExistingDuplicateSetup).objectReferenceValue = null; setupsList.index = indexOfExistingDuplicateSetup; ReorderableList.defaultBehaviours.DoRemoveButton(setupsList); setupsList.index = index; } } sdkManager.ManageVRSettings(false); } GUI.color = previousColor; if (EditorApplication.isPlaying && setup != null) { rect.x += rect.width + ReorderableList.Defaults.padding; rect.width = buttonGUIContentWidth; if (sdkManager.loadedSetup == setup) { if (GUI.Button(rect, unloadButtonGUIContent)) { sdkManager.UnloadSDKSetup(); } } else { if (GUI.Button(rect, loadButtonGUIContent)) { sdkManager.TryLoadSDKSetup(index, true, sdkManager.setups); } } } }, onAddCallback = list => { SerializedProperty serializedProperty = list.serializedProperty; int index = serializedProperty.arraySize; serializedProperty.arraySize++; list.index = index; SerializedProperty element = serializedProperty.GetArrayElementAtIndex(index); element.objectReferenceValue = null; sdkManager.ManageVRSettings(false); }, onRemoveCallback = list => { int index = list.index; VRTK_SDKSetup sdkSetup = sdkManager.setups[index]; bool isLoaded = sdkManager.loadedSetup == sdkSetup; if (isLoaded) { sdkManager.UnloadSDKSetup(); } if (sdkSetup != null) { list.serializedProperty.GetArrayElementAtIndex(index).objectReferenceValue = null; } ReorderableList.defaultBehaviours.DoRemoveButton(list); sdkManager.ManageVRSettings(false); }, onReorderCallback = list => sdkManager.ManageVRSettings(false) }; Undo.undoRedoPerformed += UndoRedoPerformed; } protected virtual void OnDisable() { Undo.undoRedoPerformed -= UndoRedoPerformed; setupsList = null; } public override void OnInspectorGUI() { serializedObject.Update(); VRTK_SDKManager sdkManager = (VRTK_SDKManager)target; const string manageNowButtonText = "Manage Now"; using (new EditorGUILayout.VerticalScope("Box")) { VRTK_EditorUtilities.AddHeader("Scripting Define Symbols", false); using (new EditorGUILayout.HorizontalScope()) { EditorGUI.BeginChangeCheck(); bool autoManage = EditorGUILayout.Toggle( VRTK_EditorUtilities.BuildGUIContent("autoManageScriptDefines", "Auto Manage"), sdkManager.autoManageScriptDefines, GUILayout.ExpandWidth(false) ); if (EditorGUI.EndChangeCheck()) { serializedObject.FindProperty("autoManageScriptDefines").boolValue = autoManage; serializedObject.ApplyModifiedProperties(); sdkManager.ManageScriptingDefineSymbols(false, true); } using (new EditorGUI.DisabledGroupScope(sdkManager.autoManageScriptDefines)) { GUIContent manageNowGUIContent = new GUIContent( manageNowButtonText, "Manage the scripting define symbols defined by the installed SDKs." + (sdkManager.autoManageScriptDefines ? "\n\nThis button is disabled because the SDK Manager is set up to manage the scripting define symbols automatically." + " Disable the checkbox on the left to allow managing them manually instead." : "") ); if (GUILayout.Button(manageNowGUIContent, GUILayout.MaxHeight(GUI.skin.label.CalcSize(manageNowGUIContent).y))) { sdkManager.ManageScriptingDefineSymbols(true, true); } } } using (new EditorGUILayout.VerticalScope("Box")) { VRTK_EditorUtilities.AddHeader("Active Symbols Without SDK Classes", false); VRTK_SDKInfo[] availableSDKInfos = VRTK_SDKManager .AvailableSystemSDKInfos .Concat(VRTK_SDKManager.AvailableBoundariesSDKInfos) .Concat(VRTK_SDKManager.AvailableHeadsetSDKInfos) .Concat(VRTK_SDKManager.AvailableControllerSDKInfos) .ToArray(); HashSet sdkSymbols = new HashSet(availableSDKInfos.Select(info => info.description.symbol)); IGrouping[] availableGroupedPredicateInfos = VRTK_SDKManager .AvailableScriptingDefineSymbolPredicateInfos .GroupBy(info => info.attribute.buildTargetGroup) .ToArray(); foreach (IGrouping grouping in availableGroupedPredicateInfos) { VRTK_SDKManager.ScriptingDefineSymbolPredicateInfo[] possibleActiveInfos = grouping .Where(info => !sdkSymbols.Contains(info.attribute.symbol) && grouping.Except(new[] { info }) .All(predicateInfo => !(predicateInfo.methodInfo == info.methodInfo && sdkSymbols.Contains(predicateInfo.attribute.symbol)))) .OrderBy(info => info.attribute.symbol) .ToArray(); if (possibleActiveInfos.Length == 0) { continue; } EditorGUI.indentLevel++; BuildTargetGroup targetGroup = grouping.Key; isBuildTargetActiveSymbolsFoldOut[targetGroup] = EditorGUI.Foldout( EditorGUILayout.GetControlRect(), isBuildTargetActiveSymbolsFoldOut[targetGroup], targetGroup.ToString(), true ); if (isBuildTargetActiveSymbolsFoldOut[targetGroup]) { foreach (VRTK_SDKManager.ScriptingDefineSymbolPredicateInfo predicateInfo in possibleActiveInfos) { int symbolIndex = sdkManager .activeScriptingDefineSymbolsWithoutSDKClasses .FindIndex(attribute => attribute.symbol == predicateInfo.attribute.symbol); string symbolLabel = predicateInfo.attribute.symbol.Remove( 0, SDK_ScriptingDefineSymbolPredicateAttribute.RemovableSymbolPrefix.Length ); if (!(bool)predicateInfo.methodInfo.Invoke(null, null)) { symbolLabel += " (not installed)"; } EditorGUI.BeginChangeCheck(); bool isSymbolActive = EditorGUILayout.ToggleLeft(symbolLabel, symbolIndex != -1); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(sdkManager, "Active Symbol Change"); if (isSymbolActive) { sdkManager.activeScriptingDefineSymbolsWithoutSDKClasses.Add(predicateInfo.attribute); } else { sdkManager.activeScriptingDefineSymbolsWithoutSDKClasses.RemoveAt(symbolIndex); } sdkManager.ManageScriptingDefineSymbols(false, true); } } } EditorGUI.indentLevel--; } } VRTK_EditorUtilities.DrawUsingDestructiveStyle(GUI.skin.button, style => { GUIContent clearSymbolsGUIContent = new GUIContent( "Remove All Symbols", "Remove all scripting define symbols of VRTK. This is handy if you removed the SDK files from your project but still have" + " the symbols defined which results in compile errors." + "\nIf you have the above checkbox enabled the symbols will be managed automatically after clearing them. Otherwise hit the" + " '" + manageNowButtonText + "' button to add the symbols for the currently installed SDKs again." ); if (GUILayout.Button(clearSymbolsGUIContent, style)) { BuildTargetGroup[] targetGroups = VRTK_SharedMethods.GetValidBuildTargetGroups(); foreach (BuildTargetGroup targetGroup in targetGroups) { string[] currentSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup) .Split(';') .Distinct() .OrderBy(symbol => symbol, StringComparer.Ordinal) .ToArray(); string[] newSymbols = currentSymbols .Where(symbol => !symbol.StartsWith(SDK_ScriptingDefineSymbolPredicateAttribute.RemovableSymbolPrefix, StringComparison.Ordinal)) .ToArray(); if (currentSymbols.SequenceEqual(newSymbols)) { continue; } PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, string.Join(";", newSymbols)); string[] removedSymbols = currentSymbols.Except(newSymbols).ToArray(); if (removedSymbols.Length > 0) { VRTK_Logger.Info(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.SCRIPTING_DEFINE_SYMBOLS_REMOVED, targetGroup, string.Join(", ", removedSymbols))); } } } }); } using (new EditorGUILayout.VerticalScope("Box")) { VRTK_EditorUtilities.AddHeader("Script Aliases", false); EditorGUILayout.PropertyField( serializedObject.FindProperty("scriptAliasLeftController"), VRTK_EditorUtilities.BuildGUIContent("scriptAliasLeftController", "Left Controller") ); EditorGUILayout.PropertyField( serializedObject.FindProperty("scriptAliasRightController"), VRTK_EditorUtilities.BuildGUIContent("scriptAliasRightController", "Right Controller") ); } using (new EditorGUILayout.VerticalScope("Box")) { VRTK_EditorUtilities.AddHeader("Setups", false); using (new EditorGUILayout.HorizontalScope()) { EditorGUI.BeginChangeCheck(); bool autoManage = EditorGUILayout.Toggle( VRTK_EditorUtilities.BuildGUIContent("autoManageVRSettings"), sdkManager.autoManageVRSettings, GUILayout.ExpandWidth(false) ); if (EditorGUI.EndChangeCheck()) { serializedObject.FindProperty("autoManageVRSettings").boolValue = autoManage; serializedObject.ApplyModifiedProperties(); sdkManager.ManageVRSettings(false); } using (new EditorGUI.DisabledGroupScope(sdkManager.autoManageVRSettings)) { GUIContent manageNowGUIContent = new GUIContent( manageNowButtonText, "Manage the VR settings of the Player Settings to allow for all the installed SDKs." + (sdkManager.autoManageVRSettings ? "\n\nThis button is disabled because the SDK Manager is set up to manage the VR Settings automatically." + " Disable the checkbox on the left to allow managing them manually instead." : "") ); if (GUILayout.Button(manageNowGUIContent, GUILayout.MaxHeight(GUI.skin.label.CalcSize(manageNowGUIContent).y))) { sdkManager.ManageVRSettings(true); } } } EditorGUILayout.PropertyField( serializedObject.FindProperty("autoLoadSetup"), VRTK_EditorUtilities.BuildGUIContent("autoLoadSetup", "Auto Load") ); setupsList.DoLayoutList(); GUIContent autoPopulateGUIContent = new GUIContent("Auto Populate", "Automatically populates the list of SDK Setups with Setups in the scene."); if (GUILayout.Button(autoPopulateGUIContent)) { SerializedProperty serializedProperty = setupsList.serializedProperty; serializedProperty.ClearArray(); VRTK_SDKSetup[] setups = sdkManager.GetComponentsInChildren(true) .Concat(VRTK_SharedMethods.FindEvenInactiveComponents(true)) .Distinct() .ToArray(); for (int index = 0; index < setups.Length; index++) { VRTK_SDKSetup setup = setups[index]; serializedProperty.InsertArrayElementAtIndex(index); serializedProperty.GetArrayElementAtIndex(index).objectReferenceValue = setup; } } if (sdkManager.setups.Length > 1) { EditorGUILayout.HelpBox("Duplicated setups are removed automatically.", MessageType.Info); } if (Enumerable.Range(0, sdkManager.setups.Length).Any(IsSDKSetupNeverUsed)) { EditorGUILayout.HelpBox("Gray setups will never be loaded because either the SDK Setup isn't valid or there" + " is a valid Setup before it that uses any non-VR SDK.", MessageType.Warning); } } using (new EditorGUILayout.VerticalScope("Box")) { VRTK_EditorUtilities.AddHeader("Target Platform Group Exclusions", false); SerializedProperty excludeTargetGroups = serializedObject.FindProperty("excludeTargetGroups"); excludeTargetGroups.arraySize = EditorGUILayout.IntField("Size", excludeTargetGroups.arraySize); for (int i = 0; i < excludeTargetGroups.arraySize; i++) { EditorGUILayout.PropertyField(excludeTargetGroups.GetArrayElementAtIndex(i)); } } EditorGUILayout.PropertyField(serializedObject.FindProperty("persistOnLoad")); serializedObject.ApplyModifiedProperties(); } private void UndoRedoPerformed() { VRTK_SDKManager sdkManager = (VRTK_SDKManager)target; sdkManager.ManageVRSettings(false); sdkManager.ManageScriptingDefineSymbols(false, false); } private bool IsSDKSetupNeverUsed(int sdkSetupIndex) { VRTK_SDKSetup[] setups = ((VRTK_SDKManager)target).setups; VRTK_SDKSetup setup = setups[sdkSetupIndex]; return setup == null || !setup.isValid || Array.FindIndex( setups, 0, sdkSetupIndex, sdkSetup => sdkSetup != null && sdkSetup.isValid && sdkSetup.usedVRDeviceNames.Contains("None") ) != -1; } } }