public virtual void RenderCustomInspector()
        {
            serializedObject.Update();

            Rect position;
            bool isPlayMode = EditorApplication.isPlaying || EditorApplication.isPaused;

            #region General Settings
            EditorGUILayout.BeginHorizontal();
            InspectorUIUtility.DrawTitle("General");
            InspectorUIUtility.RenderDocLinkButton(Interactable_URL);
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            // States
            SerializedProperty states = serializedObject.FindProperty("States");

            // If states value is not provided, try to use Default states type
            if (states.objectReferenceValue == null)
            {
                states.objectReferenceValue = ThemeInspector.GetDefaultInteractableStates();
            }

            GUI.enabled = !isPlayMode;
            EditorGUILayout.PropertyField(states, new GUIContent("States", "The States this Interactable is based on"));
            GUI.enabled = true;

            if (states.objectReferenceValue == null)
            {
                InspectorUIUtility.DrawError("Please assign a States object!");
                EditorGUILayout.EndVertical();
                serializedObject.ApplyModifiedProperties();
                return;
            }

            //standard Interactable Object UI
            SerializedProperty enabled = serializedObject.FindProperty("Enabled");
            EditorGUILayout.PropertyField(enabled, new GUIContent("Enabled", "Is this Interactable Enabled?"));

            SerializedProperty actionId = serializedObject.FindProperty("InputActionId");

            if (actionOptions == null)
            {
                GUI.enabled = false;
                EditorGUILayout.Popup("Input Actions", 0, new string[] { "Missing Mixed Reality Toolkit" });
                GUI.enabled = true;
            }
            else
            {
                position = EditorGUILayout.GetControlRect();
                DrawDropDownProperty(position, actionId, actionOptions, new GUIContent("Input Actions", "The input action filter"));
            }

            using (new EditorGUI.IndentLevelScope())
            {
                SerializedProperty isGlobal = serializedObject.FindProperty("IsGlobal");
                EditorGUILayout.PropertyField(isGlobal, new GUIContent("Is Global", "Like a modal, does not require focus"));
            }

            SerializedProperty voiceCommands = serializedObject.FindProperty("VoiceCommand");

            // check speech commands profile for a list of commands
            if (speechKeywords == null)
            {
                GUI.enabled = false;
                EditorGUILayout.Popup("Speech Command", 0, new string[] { "Missing Speech Commands" });
                InspectorUIUtility.DrawNotice("Create speech commands in the MRTK/Input/Speech Commands Profile");
                GUI.enabled = true;
            }
            else
            {
                //look for items in the sppech commands list that match the voiceCommands string
                // this string should be empty if we are not listening to speech commands
                // will return zero if empty, to match the inserted off value.
                int currentIndex = SpeechKeywordLookup(voiceCommands.stringValue, speechKeywords);
                GUI.enabled = !isPlayMode;
                position    = EditorGUILayout.GetControlRect();
                GUIContent label = new GUIContent("Speech Command", "Speech Commands to use with Interactable, pulled from MRTK/Input/Speech Commands Profile");
                EditorGUI.BeginProperty(position, label, voiceCommands);
                {
                    currentIndex = EditorGUI.Popup(position, label, currentIndex, speechKeywords);

                    if (currentIndex > 0)
                    {
                        voiceCommands.stringValue = speechKeywords[currentIndex].text;
                    }
                    else
                    {
                        voiceCommands.stringValue = "";
                    }
                }
                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?"));
                }
            }

            SerializedProperty dimensions = serializedObject.FindProperty("Dimensions");
            // 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())
                {
                    SerializedProperty canSelect           = serializedObject.FindProperty("CanSelect");
                    SerializedProperty canDeselect         = serializedObject.FindProperty("CanDeselect");
                    SerializedProperty startDimensionIndex = serializedObject.FindProperty("StartDimensionIndex");

                    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
                            {
                                SerializedProperty dimensionIndex = serializedObject.FindProperty("dimensionIndex");
                                EditorGUI.IntField(position, CurrentDimensionLabel, dimensionIndex.intValue);
                            }
                        }
                        else if (dimensions.intValue == (int)SelectionModes.Toggle + 1)
                        {
                            // toggle
                            if (!isPlayMode)
                            {
                                bool isToggled = EditorGUI.Toggle(position, isToggledLabel, startDimensionIndex.intValue > 0);
                                startDimensionIndex.intValue = isToggled ? 1 : 0;
                            }
                            else
                            {
                                SerializedProperty dimensionIndex = serializedObject.FindProperty("dimensionIndex");
                                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();

            #endregion

            EditorGUILayout.Space();

            if (!ProfilesSetup && !showProfiles)
            {
                InspectorUIUtility.DrawWarning("Profiles (Optional) have not been set up or has errors.");
            }

            #region Profiles

            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)
            {
                for (int i = 0; i < profileList.arraySize; i++)
                {
                    EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                    // get profiles
                    SerializedProperty sItem      = profileList.GetArrayElementAtIndex(i);
                    SerializedProperty gameObject = sItem.FindPropertyRelative("Target");
                    string             targetName = "Profile " + (i + 1);
                    if (gameObject.objectReferenceValue != null)
                    {
                        targetName = gameObject.objectReferenceValue.name;
                        validProfileCnt++;
                    }

                    EditorGUILayout.BeginHorizontal();

                    EditorGUILayout.PropertyField(gameObject, new GUIContent("Target", "Target gameObject for this theme properties to manipulate"));
                    bool triggered = InspectorUIUtility.SmallButton(new GUIContent(InspectorUIUtility.Minus, "Remove Profile"), i, RemoveProfile);

                    if (triggered)
                    {
                        continue;
                    }

                    EditorGUILayout.EndHorizontal();

                    // get themes
                    SerializedProperty themes = sItem.FindPropertyRelative("Themes");

                    // make sure there are enough themes as dimensions
                    if (themes.arraySize > dimensions.intValue)
                    {
                        // make sure there are not more themes than dimensions
                        int cnt = themes.arraySize - 1;
                        for (int j = cnt; j > dimensions.intValue - 1; j--)
                        {
                            themes.DeleteArrayElementAtIndex(j);
                        }
                    }

                    // add themes when increasing dimensions
                    if (themes.arraySize < dimensions.intValue)
                    {
                        int cnt = themes.arraySize;
                        for (int j = cnt; j < dimensions.intValue; j++)
                        {
                            themes.InsertArrayElementAtIndex(themes.arraySize);
                            SerializedProperty theme = themes.GetArrayElementAtIndex(themes.arraySize - 1);

                            string[] themeLocations = AssetDatabase.FindAssets("DefaultTheme");
                            if (themeLocations.Length > 0)
                            {
                                for (int k = 0; k < themeLocations.Length; k++)
                                {
                                    string path         = AssetDatabase.GUIDToAssetPath(themeLocations[k]);
                                    Theme  defaultTheme = (Theme)AssetDatabase.LoadAssetAtPath(path, typeof(Theme));
                                    if (defaultTheme != null)
                                    {
                                        theme.objectReferenceValue = defaultTheme;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    // Render all themes for current target
                    for (int t = 0; t < themes.arraySize; t++)
                    {
                        SerializedProperty themeItem  = themes.GetArrayElementAtIndex(t);
                        string             themeLabel = BuildThemeTitle(selectionMode, t);

                        EditorGUILayout.PropertyField(themeItem, new GUIContent(themeLabel, "Theme properties for interaction feedback"));

                        if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue)
                        {
                            if (themeItem.objectReferenceValue.name == "DefaultTheme")
                            {
                                EditorGUILayout.BeginHorizontal();
                                InspectorUIUtility.DrawWarning("DefaultTheme should not be edited.  ");
                                bool newTheme = InspectorUIUtility.FlexButton(new GUIContent("Create Theme", "Create a new theme"), new int[] { i, t, 0 }, CreateTheme);
                                if (newTheme)
                                {
                                    continue;
                                }
                                EditorGUILayout.EndHorizontal();
                            }

                            SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme");
                            hadDefault.boolValue = true;
                            string prefKey      = themeItem.objectReferenceValue.name + "Profiles" + i + "_Theme" + t + "_Edit";
                            bool   hasPref      = EditorPrefs.HasKey(prefKey);
                            bool   showSettings = EditorPrefs.GetBool(prefKey);
                            if (!hasPref)
                            {
                                showSettings = true;
                            }

                            InspectorUIUtility.ListSettings settings = listSettings[i];
                            bool show = InspectorUIUtility.DrawSectionFoldout(themeItem.objectReferenceValue.name + " (Click to edit)", showSettings, FontStyle.Normal);

                            if (show != showSettings)
                            {
                                EditorPrefs.SetBool(prefKey, show);
                                settings.Show = show;
                            }

                            if (show)
                            {
                                SerializedObject   themeObj         = new SerializedObject(themeItem.objectReferenceValue);
                                SerializedProperty themeObjSettings = themeObj.FindProperty("Settings");
                                themeObj.Update();

                                GUILayout.Space(5);

                                if (themeObjSettings.arraySize < 1)
                                {
                                    AddThemeProperty(new int[] { i, t, 0 });
                                }

                                int[]   location = new int[] { i, t, 0 };
                                State[] iStates  = GetStates();

                                ThemeInspector.RenderThemeSettings(themeObjSettings, themeObj, themeOptions, gameObject, location, iStates, ThemePropertiesBoxMargin);
                                InspectorUIUtility.FlexButton(new GUIContent("+", "Add Theme Property"), location, AddThemeProperty);
                                ThemeInspector.RenderThemeStates(themeObjSettings, iStates, ThemePropertiesBoxMargin);

                                themeObj.ApplyModifiedProperties();
                            }
                            listSettings[i] = settings;

                            validProfileCnt++;
                        }
                        else
                        {
                            // show message about profile setup
                            string themeMsg = "Assign a ";
                            if (gameObject.objectReferenceValue == null)
                            {
                                themeMsg += "Target ";
                            }

                            if (themeItem.objectReferenceValue == null)
                            {
                                if (gameObject.objectReferenceValue == null)
                                {
                                    themeMsg += "and ";
                                }
                                themeMsg += "Theme ";
                            }

                            themeMsg += "above to add visual effects";
                            SerializedProperty hadDefault = sItem.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;
                    }

                    EditorGUILayout.EndVertical();
                }// 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;

            #endregion

            EditorGUILayout.Space();

            #region Events settings

            bool isEventsOpen = InspectorUIUtility.DrawSectionFoldout("Events", showEvents, FontStyle.Bold, InspectorUIUtility.TitleFontSize);
            if (showEvents != isEventsOpen)
            {
                showEvents = isEventsOpen;
                EditorPrefs.SetBool(ShowEventsPrefKey, showEvents);
            }
            EditorGUILayout.Space();

            if (showEvents)
            {
                SerializedProperty onClick = serializedObject.FindProperty("OnClick");
                EditorGUILayout.PropertyField(onClick, new GUIContent("OnClick"));

                SerializedProperty events = serializedObject.FindProperty("Events");
                GUI.enabled = !isPlayMode;
                for (int i = 0; i < events.arraySize; i++)
                {
                    SerializedProperty eventItem = events.GetArrayElementAtIndex(i);
                    InteractableReceiverListInspector.RenderEventSettings(eventItem, i, eventOptions, ChangeEvent, RemoveEvent);
                }
                GUI.enabled = true;

                if (eventOptions.ClassNames.Length > 1)
                {
                    if (GUILayout.Button(new GUIContent("Add Event")))
                    {
                        AddEvent(events.arraySize);
                    }
                }
            }

            #endregion

            serializedObject.ApplyModifiedProperties();
        }