protected virtual SerializedObject ChangeThemeProperty(int index, SerializedObject themeObj, SerializedProperty target, bool isNew = false) { SerializedProperty themeObjSettings = themeObj.FindProperty("Settings"); themeObjSettings = ThemeInspector.ChangeThemeProperty(index, themeObjSettings, target, GetStates(), isNew); return(themeObj); }
protected virtual void AddThemeProperty(SerializedProperty profileItem, SerializedProperty themeItem) { SerializedProperty serializedTarget = profileItem.FindPropertyRelative("Target"); SerializedObject themeObj = new SerializedObject(themeItem.objectReferenceValue); themeObj.Update(); SerializedProperty themeObjSettings = themeObj.FindProperty("Settings"); themeObjSettings.InsertArrayElementAtIndex(themeObjSettings.arraySize); SerializedProperty settingsItem = themeObjSettings.GetArrayElementAtIndex(themeObjSettings.arraySize - 1); SerializedProperty className = settingsItem.FindPropertyRelative("Name"); SerializedProperty assemblyQualifiedName = settingsItem.FindPropertyRelative("AssemblyQualifiedName"); if (themeObjSettings.arraySize == 1) { className.stringValue = "ScaleOffsetColorTheme"; assemblyQualifiedName.stringValue = typeof(ScaleOffsetColorTheme).AssemblyQualifiedName; } else { className.stringValue = themeOptions.ClassNames[0]; assemblyQualifiedName.stringValue = themeOptions.AssemblyQualifiedNames[0]; } SerializedProperty easing = settingsItem.FindPropertyRelative("Easing"); SerializedProperty time = easing.FindPropertyRelative("LerpTime"); SerializedProperty curve = easing.FindPropertyRelative("Curve"); time.floatValue = 0.5f; curve.animationCurveValue = AnimationCurve.Linear(0, 1, 1, 1); themeObjSettings = ThemeInspector.ChangeThemeProperty(themeObjSettings.arraySize - 1, themeObjSettings, serializedTarget, GetStates(), true); themeObj.ApplyModifiedProperties(); }
protected void RenderGeneralSettings() { Rect position; bool isPlayMode = EditorApplication.isPlaying || EditorApplication.isPaused; using (new EditorGUILayout.HorizontalScope()) { InspectorUIUtility.DrawTitle("General"); if (target != null) { var helpURL = target.GetType().GetCustomAttribute <HelpURLAttribute>(); if (helpURL != null) { InspectorUIUtility.RenderDocumentationButton(helpURL.URL); } } } EditorGUILayout.BeginVertical(EditorStyles.helpBox); // States // If states value is not provided, try to use Default states type if (statesProperty.objectReferenceValue == null) { statesProperty.objectReferenceValue = ThemeInspector.GetDefaultInteractableStates(); } GUI.enabled = !isPlayMode; EditorGUILayout.PropertyField(statesProperty, new GUIContent("States", "The States this Interactable is based on")); GUI.enabled = true; if (statesProperty.objectReferenceValue == null) { InspectorUIUtility.DrawError("Please assign a States object!"); serializedObject.ApplyModifiedProperties(); return; } EditorGUILayout.PropertyField(enabledProperty, new GUIContent("Enabled", "Is this Interactable Enabled?")); // Input Actions bool validActionOptions = inputActionOptions != null; GUI.enabled = validActionOptions && !isPlayMode; var actionOptions = validActionOptions ? inputActionOptions : new string[] { "Missing Mixed Reality Toolkit" }; DrawDropDownProperty(EditorGUILayout.GetControlRect(), actionId, actionOptions, InputActionsLabel); GUI.enabled = true; using (new EditorGUI.IndentLevelScope()) { EditorGUILayout.PropertyField(isGlobal, new GUIContent("Is Global", "Like a modal, does not require focus")); } // Speech keywords bool validSpeechKeywords = speechKeywordOptions != null; GUI.enabled = validSpeechKeywords && !isPlayMode; string[] keywordOptions = validSpeechKeywords ? speechKeywordOptions : new string[] { "Missing Speech Commands" }; int currentIndex = validSpeechKeywords ? SpeechKeywordLookup(voiceCommands.stringValue, speechKeywordOptions) : 0; position = EditorGUILayout.GetControlRect(); //BeginProperty allows tracking of serialized properties for bolding prefab changes etc EditorGUI.BeginProperty(position, SpeechComamndsLabel, voiceCommands); { currentIndex = EditorGUI.Popup(position, SpeechComamndsLabel.text, currentIndex, keywordOptions); if (validSpeechKeywords) { voiceCommands.stringValue = currentIndex > 0 ? speechKeywordOptions[currentIndex] : string.Empty; } } EditorGUI.EndProperty(); GUI.enabled = true; // show requires gaze because voice command has a value if (!string.IsNullOrEmpty(voiceCommands.stringValue)) { using (new EditorGUI.IndentLevelScope()) { SerializedProperty requireGaze = serializedObject.FindProperty("RequiresFocus"); EditorGUILayout.PropertyField(requireGaze, new GUIContent("Requires Focus", "Does the voice command require gazing at this interactable?")); } } // should be 1 or more dimensions.intValue = Mathf.Clamp(dimensions.intValue, 1, 9); string[] selectionModeNames = Enum.GetNames(typeof(SelectionModes)); // clamp to values in the enum int selectionModeIndex = Mathf.Clamp(dimensions.intValue, 1, selectionModeNames.Length) - 1; // user-friendly dimension settings SelectionModes selectionMode = SelectionModes.Button; position = EditorGUILayout.GetControlRect(); GUI.enabled = !isPlayMode; EditorGUI.BeginProperty(position, selectionModeLabel, dimensions); { selectionMode = (SelectionModes)EditorGUI.EnumPopup(position, selectionModeLabel, (SelectionModes)(selectionModeIndex)); switch (selectionMode) { case SelectionModes.Button: dimensions.intValue = 1; break; case SelectionModes.Toggle: dimensions.intValue = 2; break; case SelectionModes.MultiDimension: // multi dimension mode - set min value to 3 dimensions.intValue = Mathf.Max(3, dimensions.intValue); position = EditorGUILayout.GetControlRect(); dimensions.intValue = EditorGUI.IntField(position, dimensionsLabel, dimensions.intValue); break; default: break; } } EditorGUI.EndProperty(); if (dimensions.intValue > 1) { // toggle or multi dimensional button using (new EditorGUI.IndentLevelScope()) { EditorGUILayout.PropertyField(canSelect, new GUIContent("Can Select", "The user can toggle this button")); EditorGUILayout.PropertyField(canDeselect, new GUIContent("Can Deselect", "The user can untoggle this button, set false for a radial interaction.")); position = EditorGUILayout.GetControlRect(); EditorGUI.BeginProperty(position, startDimensionLabel, startDimensionIndex); { if (dimensions.intValue >= selectionModeNames.Length) { // multi dimensions if (!isPlayMode) { startDimensionIndex.intValue = EditorGUI.IntField(position, startDimensionLabel, startDimensionIndex.intValue); } else { EditorGUI.IntField(position, CurrentDimensionLabel, dimensionIndex.intValue); } } else if (dimensions.intValue == (int)SelectionModes.Toggle + 1) { if (!isPlayMode) { bool isToggled = EditorGUI.Toggle(position, isToggledLabel, startDimensionIndex.intValue > 0); startDimensionIndex.intValue = isToggled ? 1 : 0; } else { bool isToggled = EditorGUI.Toggle(position, isToggledLabel, dimensionIndex.intValue > 0); } } startDimensionIndex.intValue = Mathf.Clamp(startDimensionIndex.intValue, 0, dimensions.intValue - 1); } EditorGUI.EndProperty(); } } GUI.enabled = true; EditorGUILayout.EndVertical(); }
private void RenderProfileSettings() { if (!ProfilesSetup && !showProfiles) { InspectorUIUtility.DrawWarning("Profiles (Optional) have not been set up or has errors."); } bool isProfilesOpen = InspectorUIUtility.DrawSectionFoldout("Profiles", showProfiles, FontStyle.Bold, InspectorUIUtility.TitleFontSize); if (showProfiles != isProfilesOpen) { showProfiles = isProfilesOpen; EditorPrefs.SetBool(ShowProfilesPrefKey, showProfiles); } if (profileList.arraySize < 1) { AddProfile(0); } int validProfileCnt = 0; int themeCnt = 0; if (showProfiles) { // Render all profile items for (int i = 0; i < profileList.arraySize; i++) { using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { SerializedProperty profileItem = profileList.GetArrayElementAtIndex(i); SerializedProperty gameObject = profileItem.FindPropertyRelative("Target"); using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.PropertyField(gameObject, new GUIContent("Target", "Target gameObject for this theme properties to manipulate")); if (InspectorUIUtility.SmallButton(new GUIContent(InspectorUIUtility.Minus, "Remove Profile"), i, RemoveProfile)) { continue; } } SerializedProperty themes = profileItem.FindPropertyRelative("Themes"); ValidateThemes(dimensions, themes); // Render all themes for current target for (int t = 0; t < themes.arraySize; t++) { SerializedProperty themeItem = themes.GetArrayElementAtIndex(t); string themeLabel = BuildThemeTitle(dimensions.intValue, t); EditorGUILayout.PropertyField(themeItem, new GUIContent(themeLabel, "Theme properties for interaction feedback")); if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue) { RenderDefaultThemeWarning(profileItem, themeItem); SerializedProperty hadDefault = profileItem.FindPropertyRelative("HadDefaultTheme"); hadDefault.boolValue = true; string prefKey = themeItem.objectReferenceValue.name + "Profiles" + i + "_Theme" + t + "_Edit"; bool showSettingsPref = EditorPrefs.GetBool(prefKey, true); bool show = InspectorUIUtility.DrawSectionFoldout(themeItem.objectReferenceValue.name + " (Click to edit)", showSettingsPref, FontStyle.Normal); if (show != showSettingsPref) { EditorPrefs.SetBool(prefKey, show); } if (show) { SerializedObject themeObj = new SerializedObject(themeItem.objectReferenceValue); SerializedProperty themeObjSettings = themeObj.FindProperty("Settings"); GUILayout.Space(5); if (InspectorUIUtility.FlexButton(AddThemePropertyLabel)) { AddThemeProperty(profileItem, themeItem); } State[] states = GetStates(); themeObj.Update(); ThemeInspector.RenderThemeSettings(themeObjSettings, themeOptions, gameObject, states, ThemePropertiesBoxMargin); ThemeInspector.RenderThemeStates(themeObjSettings, states, ThemePropertiesBoxMargin); themeObj.ApplyModifiedProperties(); } validProfileCnt++; } else { // show message about profile setup const string themeMsg = "Assign a Target and/or Theme above to add visual effects"; SerializedProperty hadDefault = profileItem.FindPropertyRelative("HadDefaultTheme"); if (!hadDefault.boolValue && t == 0) { string[] themeLocations = AssetDatabase.FindAssets("DefaultTheme"); if (themeLocations.Length > 0) { for (int j = 0; j < themeLocations.Length; j++) { string path = AssetDatabase.GUIDToAssetPath(themeLocations[0]); Theme defaultTheme = (Theme)AssetDatabase.LoadAssetAtPath(path, typeof(Theme)); if (defaultTheme != null) { themeItem.objectReferenceValue = defaultTheme; break; } } if (themeItem.objectReferenceValue != null) { hadDefault.boolValue = true; } } else { InspectorUIUtility.DrawError("DefaultTheme missing from project!"); } } InspectorUIUtility.DrawError(themeMsg); } themeCnt += themes.arraySize; } } }// profile for loop if (GUILayout.Button(new GUIContent("Add Profile"))) { AddProfile(profileList.arraySize); } } else { // make sure profiles are setup if closed by default for (int i = 0; i < profileList.arraySize; i++) { SerializedProperty sItem = profileList.GetArrayElementAtIndex(i); SerializedProperty gameObject = sItem.FindPropertyRelative("Target"); SerializedProperty themes = sItem.FindPropertyRelative("Themes"); if (gameObject.objectReferenceValue != null) { validProfileCnt++; } for (int t = 0; t < themes.arraySize; t++) { SerializedProperty themeItem = themes.GetArrayElementAtIndex(themes.arraySize - 1); if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue) { validProfileCnt++; SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); hadDefault.boolValue = true; } } themeCnt += themes.arraySize; } } ProfilesSetup = validProfileCnt == profileList.arraySize + themeCnt; }