/// <summary>
        /// Helper function to render header correctly for all profiles
        /// </summary>
        /// <param name="title">Title of profile</param>
        /// <param name="description">profile tooltip describing purpose</param>
        /// <param name="selectionObject">The profile object. Used to re-select the object after MRTK instance is created.</param>
        /// <param name="isProfileInitialized">profile properties are full initialized for rendering</param>
        /// <param name="backText">Text for back button if not rendering as sub-profile</param>
        /// <param name="backProfile">Target profile to return to if not rendering as sub-profile</param>
        protected void RenderProfileHeader(string title, string description, Object selectionObject, bool isProfileInitialized = true, BackProfileType returnProfileTarget = BackProfileType.Configuration)
        {
            RenderMRTKLogo();

            var profile = target as BaseMixedRealityProfile;

            if (!RenderAsSubProfile)
            {
                CheckEditorPlayMode();

                if (!profile.IsCustomProfile)
                {
                    EditorGUILayout.HelpBox("Default MRTK profiles cannot be edited. Create a clone of this profile to modify settings.", MessageType.Warning);
                    if (GUILayout.Button(new GUIContent("Clone")))
                    {
                        MixedRealityProfileCloneWindow.OpenWindow(null, (BaseMixedRealityProfile)target, null);
                    }
                }

                if (IsProfileInActiveInstance())
                {
                    DrawBacktrackProfileButton(returnProfileTarget);
                }

                if (!isProfileInitialized)
                {
                    if (!MixedRealityToolkit.IsInitialized)
                    {
                        EditorGUILayout.HelpBox("There is not a MRTK instance in your scene. Some properties may not be editable", MessageType.Error);
                        if (InspectorUIUtility.RenderIndentedButton(new GUIContent("Add Mixed Reality Toolkit instance to scene"), EditorStyles.miniButton))
                        {
                            MixedRealityInspectorUtility.AddMixedRealityToolkitToScene(MixedRealityInspectorUtility.GetDefaultConfigProfile());
                            // After the toolkit has been created, set the selection back to this item so the user doesn't get lost
                            Selection.activeObject = selectionObject;
                        }
                    }
                    else if (!MixedRealityToolkit.Instance.HasActiveProfile)
                    {
                        EditorGUILayout.HelpBox("There is no active profile assigned in the current MRTK instance. Some properties may not be editable.", MessageType.Error);
                    }
                }
            }
            else
            {
                if (!isProfileInitialized && profile.IsCustomProfile)
                {
                    EditorGUILayout.HelpBox("Some properties may not be editable in this profile. Please refer to the error messages below to resolve editing.", MessageType.Warning);
                }
            }

            using (new EditorGUILayout.HorizontalScope())
            {
                EditorGUILayout.LabelField(new GUIContent(title, description), EditorStyles.boldLabel, GUILayout.ExpandWidth(true));
                RenderDocumentation(selectionObject);
            }

            EditorGUILayout.LabelField(string.Empty, GUI.skin.horizontalSlider);
        }
        public static void OpenWindow(BaseMixedRealityProfile parentProfile, BaseMixedRealityProfile childProfile, SerializedProperty childProperty)
        {
            if (cloneWindow != null)
            {
                cloneWindow.Close();
            }

            cloneWindow = (MixedRealityProfileCloneWindow)GetWindow <MixedRealityProfileCloneWindow>(true, "Clone Profile", true);
            cloneWindow.Initialize(parentProfile, childProfile, childProperty);
            cloneWindow.Show(true);
        }
        public override void OnInspectorGUI()
        {
            var configurationProfile = (MixedRealityToolkitConfigurationProfile)target;

            serializedObject.Update();

            RenderMRTKLogo();

            CheckEditorPlayMode();

            if (!MixedRealityToolkit.IsInitialized)
            {
                EditorGUILayout.HelpBox("No Mixed Reality Toolkit found in scene.", MessageType.Warning);
                if (InspectorUIUtility.RenderIndentedButton("Add Mixed Reality Toolkit instance to scene"))
                {
                    MixedRealityInspectorUtility.AddMixedRealityToolkitToScene(configurationProfile);
                }
            }

            if (!configurationProfile.IsCustomProfile)
            {
                EditorGUILayout.HelpBox("The Mixed Reality Toolkit's core SDK profiles can be used to get up and running quickly.\n\n" +
                                        "You can use the default profiles provided, copy and customize the default profiles, or create your own.", MessageType.Warning);
                EditorGUILayout.BeginHorizontal();

                if (GUILayout.Button("Copy & Customize"))
                {
                    SerializedProperty targetProperty  = null;
                    UnityEngine.Object selectionTarget = null;
                    // If we have an active MRTK instance, find its config profile serialized property
                    if (MixedRealityToolkit.IsInitialized)
                    {
                        selectionTarget = MixedRealityToolkit.Instance;
                        SerializedObject mixedRealityToolkitObject = new SerializedObject(MixedRealityToolkit.Instance);
                        targetProperty = mixedRealityToolkitObject.FindProperty("activeProfile");
                    }
                    MixedRealityProfileCloneWindow.OpenWindow(null, target as BaseMixedRealityProfile, targetProperty, selectionTarget);
                }

                if (MixedRealityToolkit.IsInitialized)
                {
                    if (GUILayout.Button("Create new profiles"))
                    {
                        ScriptableObject profile = CreateInstance(nameof(MixedRealityToolkitConfigurationProfile));
                        var newProfile           = profile.CreateAsset("Assets/MixedRealityToolkit.Generated/CustomProfiles") as MixedRealityToolkitConfigurationProfile;
                        UnityEditor.Undo.RecordObject(MixedRealityToolkit.Instance, "Create new profiles");
                        MixedRealityToolkit.Instance.ActiveProfile = newProfile;
                        Selection.activeObject = newProfile;
                    }
                }

                EditorGUILayout.EndHorizontal();
                EditorGUILayout.LabelField(string.Empty, GUI.skin.horizontalSlider);
            }

            bool isGUIEnabled = !IsProfileLock((BaseMixedRealityProfile)target) && GUI.enabled;

            GUI.enabled = isGUIEnabled;

            EditorGUI.BeginChangeCheck();
            bool changed = false;

            // Experience configuration
            ExperienceScale experienceScale = (ExperienceScale)targetExperienceScale.intValue;

            EditorGUILayout.PropertyField(targetExperienceScale, TargetScaleContent);

            string scaleDescription = GetExperienceDescription(experienceScale);

            if (!string.IsNullOrEmpty(scaleDescription))
            {
                EditorGUILayout.HelpBox(scaleDescription, MessageType.Info);
                EditorGUILayout.Space();
            }

            changed |= EditorGUI.EndChangeCheck();

            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Width(100));
            GUI.enabled = true; // Force enable so we can view profile defaults

            int prefsSelectedTab = EditorPrefs.GetInt(SelectedTabPreferenceKey);

            SelectedProfileTab = GUILayout.SelectionGrid(prefsSelectedTab, ProfileTabTitles, 1, EditorStyles.boldLabel, GUILayout.MaxWidth(125));
            if (SelectedProfileTab != prefsSelectedTab)
            {
                EditorPrefs.SetInt(SelectedTabPreferenceKey, SelectedProfileTab);
            }

            GUI.enabled = isGUIEnabled;
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            using (new EditorGUI.IndentLevelScope())
            {
                changed |= RenderProfileFuncs[SelectedProfileTab]();
            }
            EditorGUILayout.EndVertical();
            EditorGUILayout.EndHorizontal();

            serializedObject.ApplyModifiedProperties();
            GUI.enabled = true;

            if (changed && MixedRealityToolkit.IsInitialized)
            {
                EditorApplication.delayCall += () => MixedRealityToolkit.Instance.ResetConfiguration(configurationProfile);
            }
        }
Пример #4
0
        private static bool RenderProfileInternal(SerializedProperty property, GUIContent guiContent, bool showAddButton, Type serviceType = null)
        {
            profile = property.serializedObject.targetObject as BaseMixedRealityProfile;

            bool changed = false;

            var oldObject = property.objectReferenceValue;

            // If we're constraining this to a service type, check whether the profile is valid
            // If it isn't, issue a warning.
            if (serviceType != null && oldObject != null)
            {
                bool profileTypeIsValid = false;

                foreach (MixedRealityServiceProfileAttribute serviceProfileAttribute in oldObject.GetType().GetCustomAttributes(typeof(MixedRealityServiceProfileAttribute), true))
                {
                    if (serviceProfileAttribute.ServiceType.IsAssignableFrom(serviceType))
                    {
                        profileTypeIsValid = true;
                        break;
                    }
                }

                if (!profileTypeIsValid)
                {
                    EditorGUILayout.HelpBox("This profile is not supported for " + serviceType.Name + ". Using an unsupported service may result in unexpected behavior.", MessageType.Warning);
                }
            }

            EditorGUILayout.BeginHorizontal();

            if (guiContent == null)
            {
                EditorGUILayout.PropertyField(property);
            }
            else
            {
                EditorGUILayout.PropertyField(property, guiContent);
            }

            if (property.objectReferenceValue == null)
            {
                if (showAddButton)
                {
                    if (GUILayout.Button(NewProfileContent, EditorStyles.miniButton, GUILayout.Width(20f)))
                    {
                        var profileTypeName = property.type.Replace("PPtr<$", string.Empty).Replace(">", string.Empty);
                        Debug.Assert(profileTypeName != null, "No Type Found");

                        ScriptableObject instance = CreateInstance(profileTypeName);
                        var newProfile            = instance.CreateAsset(AssetDatabase.GetAssetPath(Selection.activeObject)) as BaseMixedRealityProfile;
                        property.objectReferenceValue = newProfile;
                        property.serializedObject.ApplyModifiedProperties();
                        changed = true;
                    }
                }
            }
            else
            {
                var renderedProfile = property.objectReferenceValue as BaseMixedRealityProfile;
                Debug.Assert(renderedProfile != null);
                Debug.Assert(profile != null, "No profile was set in OnEnable. Did you forget to call base.OnEnable in a derived profile class?");

                if (GUILayout.Button(new GUIContent("Clone", "Replace with a copy of the default profile."), EditorStyles.miniButton, GUILayout.Width(42f)))
                {
                    MixedRealityProfileCloneWindow.OpenWindow(profile, renderedProfile, property);
                }
            }

            EditorGUILayout.EndHorizontal();

            // Check fields within profile for other nested profiles
            // Draw them when found
            if (property.objectReferenceValue != null)
            {
                Type profileType = property.objectReferenceValue.GetType();
                if (typeof(BaseMixedRealityProfile).IsAssignableFrom(profileType))
                {
                    string showFoldoutKey = GetSubProfileDropdownKey(property);
                    bool   showFoldout    = SessionState.GetBool(showFoldoutKey, false);
                    showFoldout = EditorGUILayout.Foldout(showFoldout, showFoldout ? "Hide " + property.displayName + " contents" : "Show " + property.displayName + " contents", true);

                    if (showFoldout)
                    {
                        UnityEditor.Editor subProfileEditor = UnityEditor.Editor.CreateEditor(property.objectReferenceValue);

                        // If this is a default MRTK configuration profile, ask it to render as a sub-profile
                        if (typeof(BaseMixedRealityToolkitConfigurationProfileInspector).IsAssignableFrom(subProfileEditor.GetType()))
                        {
                            BaseMixedRealityToolkitConfigurationProfileInspector configProfile = (BaseMixedRealityToolkitConfigurationProfileInspector)subProfileEditor;
                            configProfile.RenderAsSubProfile = true;
                        }

                        EditorGUI.indentLevel++;
                        EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                        subProfileEditor.OnInspectorGUI();

                        EditorGUILayout.Space();
                        EditorGUILayout.Space();

                        EditorGUILayout.EndVertical();
                        EditorGUI.indentLevel--;
                    }

                    SessionState.SetBool(showFoldoutKey, showFoldout);
                }
            }

            return(changed);
        }
        /// <summary>
        /// Renders a <see cref="Microsoft.MixedReality.Toolkit.BaseMixedRealityProfile"/>.
        /// </summary>
        /// <param name="property">the <see cref="Microsoft.MixedReality.Toolkit.BaseMixedRealityProfile"/> property.</param>
        /// <param name="showAddButton">If true, draw the clone button, if false, don't</param>
        /// <param name="renderProfileInBox">if true, render box around profile content, if false, don't</param>
        /// <param name="serviceType">Optional service type to limit available profile types.</param>
        /// <returns>True, if the profile changed.</returns>
        private static bool RenderProfileInternal(SerializedProperty property, Type profileType,
                                                  bool showAddButton, bool renderProfileInBox, Type serviceType = null)
        {
            var  profile   = property.serializedObject.targetObject as BaseMixedRealityProfile;
            bool changed   = false;
            var  oldObject = property.objectReferenceValue;

            if (profileType != null && !profileType.IsSubclassOf(typeof(BaseMixedRealityProfile)) && profileType != typeof(BaseMixedRealityProfile))
            {
                // If they've drag-and-dropped a non-profile scriptable object, set it to null.
                profileType = null;
            }

            // If we're constraining this to a service type, check whether the profile is valid
            // If it isn't, issue a warning.
            if (serviceType != null && oldObject != null)
            {
                if (!IsProfileForService(oldObject.GetType(), serviceType))
                {
                    EditorGUILayout.HelpBox("This profile is not supported for " + serviceType.Name + ". Using an unsupported service may result in unexpected behavior.", MessageType.Warning);
                }
            }

            // Find the profile type so we can limit the available object field options
            if (serviceType != null)
            {
                // If GetProfileTypesForService has a count greater than one, then it won't be possible to use
                // EditorGUILayout.ObjectField to restrict the set of profiles to a single type - in this
                // case all profiles of BaseMixedRealityProfile will be visible in the picker.
                //
                // However in the case where there is just a single profile type for the service, we can improve
                // upon the user experience by limiting the set of things that show in the picker by restricting
                // the set of profiles listed to only that type.
                profileType = GetProfileTypesForService(serviceType).FirstOrDefault();
            }

            // If the profile type is still null, just set it to base profile type
            if (profileType == null)
            {
                profileType = typeof(BaseMixedRealityProfile);
            }

            // Begin the horizontal group
            EditorGUILayout.BeginHorizontal();

            // Draw the object field with an empty label - label is kept in the foldout
            property.objectReferenceValue = EditorGUILayout.ObjectField(oldObject != null ? "" : property.displayName, oldObject, profileType, false, GUILayout.ExpandWidth(true));
            changed = (property.objectReferenceValue != oldObject);

            // Draw the clone button
            if (property.objectReferenceValue == null)
            {
                var profileTypeName = property.type.Replace("PPtr<$", string.Empty).Replace(">", string.Empty);
                if (showAddButton && IsConcreteProfileType(profileTypeName))
                {
                    if (GUILayout.Button(NewProfileContent, EditorStyles.miniButton, GUILayout.Width(20f)))
                    {
                        Debug.Assert(profileTypeName != null, "No Type Found");

                        ScriptableObject instance = CreateInstance(profileTypeName);
                        var newProfile            = instance.CreateAsset(AssetDatabase.GetAssetPath(Selection.activeObject)) as BaseMixedRealityProfile;
                        property.objectReferenceValue = newProfile;
                        property.serializedObject.ApplyModifiedProperties();
                        changed = true;
                    }
                }
            }
            else
            {
                var renderedProfile = property.objectReferenceValue as BaseMixedRealityProfile;
                Debug.Assert(renderedProfile != null);
                Debug.Assert(profile != null, "No profile was set in OnEnable. Did you forget to call base.OnEnable in a derived profile class?");

                if (GUILayout.Button(new GUIContent("Clone", "Replace with a copy of the default profile."), EditorStyles.miniButton, GUILayout.Width(42f)))
                {
                    MixedRealityProfileCloneWindow.OpenWindow(profile, renderedProfile, property);
                }
            }

            EditorGUILayout.EndHorizontal();

            if (property.objectReferenceValue != null)
            {
                UnityEditor.Editor subProfileEditor = UnityEditor.Editor.CreateEditor(property.objectReferenceValue);

                // If this is a default MRTK configuration profile, ask it to render as a sub-profile
                if (typeof(BaseMixedRealityToolkitConfigurationProfileInspector).IsAssignableFrom(subProfileEditor.GetType()))
                {
                    BaseMixedRealityToolkitConfigurationProfileInspector configProfile = (BaseMixedRealityToolkitConfigurationProfileInspector)subProfileEditor;
                    configProfile.RenderAsSubProfile = true;
                }

                var subProfile = property.objectReferenceValue as BaseMixedRealityProfile;
                if (subProfile != null && !subProfile.IsCustomProfile)
                {
                    EditorGUILayout.HelpBox("Clone this default profile to edit properties below", MessageType.Warning);
                }

                if (renderProfileInBox)
                {
                    EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                }
                else
                {
                    EditorGUILayout.BeginVertical();
                }

                EditorGUILayout.Space();
                subProfileEditor.OnInspectorGUI();
                EditorGUILayout.Space();

                EditorGUILayout.EndVertical();
            }

            return(changed);
        }