protected override void OnEnable()
        {
            base.OnEnable();

            button    = (CompressableButton)target;
            transform = button.transform;

            if (labelStyle == null)
            {
                labelStyle = new GUIStyle();
                labelStyle.normal.textColor = Color.white;
            }

            movingButtonVisuals  = serializedObject.FindProperty("movingButtonVisuals");
            distanceSpaceMode    = serializedObject.FindProperty("distanceSpaceMode");
            startPushDistance    = serializedObject.FindProperty("startPushDistance");
            maxPushDistance      = serializedObject.FindProperty("maxPushDistance");
            pressDistance        = serializedObject.FindProperty("pressDistance");
            releaseDistanceDelta = serializedObject.FindProperty("releaseDistanceDelta");

            touchable = button.GetComponent <NearInteractionTouchableSurface>();
        }
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            serializedObject.Update();

            // Ensure there is a touchable.
            if (touchable == null)
            {
                EditorGUILayout.HelpBox($"{target.GetType().Name} requires a {nameof(NearInteractionTouchableSurface)}-derived component on this game object to function.", MessageType.Warning);

                bool isUnityUI = (button.GetComponent <RectTransform>() != null);
                var  typeToAdd = isUnityUI ? typeof(NearInteractionTouchableUnityUI) : typeof(NearInteractionTouchable);

                if (GUILayout.Button($"Add {typeToAdd.Name} component"))
                {
                    Undo.RecordObject(target, string.Concat($"Add {typeToAdd.Name}"));
                    var addedComponent = button.gameObject.AddComponent(typeToAdd);
                    touchable = (NearInteractionTouchableSurface)addedComponent;
                }
                else
                {
                    // It won't work without it, return to avoid nullrefs.
                    return;
                }
            }

            // Ensure that the touchable has EventsToReceive set to Touch
            if (touchable.EventsToReceive != TouchableEventType.Touch)
            {
                EditorGUILayout.HelpBox($"The {nameof(NearInteractionTouchableSurface)}-derived component on this game object currently has its EventsToReceive set to '{touchable.EventsToReceive}'.  It must be set to 'Touch' in order for PressableButton to function properly.", MessageType.Warning);

                if (GUILayout.Button("Set EventsToReceive to 'Touch'"))
                {
                    Undo.RecordObject(touchable, string.Concat("Set EventsToReceive to Touch on ", touchable.name));
                    touchable.EventsToReceive = TouchableEventType.Touch;
                }
            }

            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(movingButtonVisuals);

            // Ensure that there is a moving button visuals in the UnityUI case.  Even if it is not visible, it must be present to receive GraphicsRaycasts.
            if (touchable is NearInteractionTouchableUnityUI)
            {
                if (movingButtonVisuals.objectReferenceValue == null)
                {
                    EditorGUILayout.HelpBox($"When used with a NearInteractionTouchableUnityUI, a MovingButtonVisuals is required, as it receives the GraphicsRaycast that allows pressing the button with near/hand interactions.  It does not need to be visible, but it must be able to receive GraphicsRaycasts.", MessageType.Warning);
                }
                else
                {
                    var movingVisualGameObject = (GameObject)movingButtonVisuals.objectReferenceValue;
                    var movingGraphic          = movingVisualGameObject.GetComponentInChildren <UnityEngine.UI.Graphic>();
                    if (movingGraphic == null)
                    {
                        EditorGUILayout.HelpBox($"When used with a NearInteractionTouchableUnityUI, the MovingButtonVisuals must contain an Image, RawImage, or other Graphic element so that it can receive a GraphicsRaycast.", MessageType.Warning);
                    }
                }
            }

            EditorGUILayout.LabelField("Press Settings", EditorStyles.boldLabel);

            EditorGUI.BeginChangeCheck();
            var currentMode = distanceSpaceMode.intValue;

            EditorGUILayout.PropertyField(distanceSpaceMode);
            // EndChangeCheck returns true when something was selected in the dropdown, but
            // doesn't necessarily mean that the value itself changed. Check for that too.
            if (EditorGUI.EndChangeCheck() && currentMode != distanceSpaceMode.intValue)
            {
                // Changing the DistanceSpaceMode requires updating the plane distance values so they stay in the same relative ratio positions
                Undo.RecordObject(target, string.Concat("Trigger Plane Distance Conversion of ", button.name));
                button.DistanceSpaceMode = (CompressableButton.SpaceMode)distanceSpaceMode.enumValueIndex;
                serializedObject.Update();
            }

            DrawPropertiesExcluding(serializedObject, excludeProperties);

            startPushDistance.floatValue = ClampStartPushDistance(startPushDistance.floatValue);

            // show button state in play mode
            {
                EditorGUI.BeginDisabledGroup(Application.isPlaying == false);

                EditorGUILayout.Space();
                EditorGUILayout.LabelField("Button State", EditorStyles.boldLabel);
                EditorGUILayout.LabelField("Current Push Distance", button.CurrentPushDistance.ToString());
                EditorGUILayout.Toggle("Touching", button.IsTouching);
                EditorGUILayout.Toggle("Pressing", button.IsPressing);
                EditorGUI.EndDisabledGroup();
            }

            // editor settings
            {
                EditorGUI.BeginDisabledGroup(Application.isPlaying == true);
                EditorGUILayout.Space();
                EditorGUILayout.LabelField("Editor Settings", EditorStyles.boldLabel);
                var prevVisiblePlanes = SessionState.GetBool(VisiblePlanesKey, true);
                VisiblePlanes = EditorGUILayout.Toggle("Show Button Event Planes", prevVisiblePlanes);
                if (VisiblePlanes != prevVisiblePlanes)
                {
                    SessionState.SetBool(VisiblePlanesKey, VisiblePlanes);
                    EditorUtility.SetDirty(target);
                }

                // enable plane editing
                {
                    EditorGUI.BeginDisabledGroup(VisiblePlanes == false);
                    var prevEditingEnabled = SessionState.GetBool(EditingEnabledKey, false);
                    EditingEnabled = EditorGUILayout.Toggle("Make Planes Editable", EditingEnabled);
                    if (EditingEnabled != prevEditingEnabled)
                    {
                        SessionState.SetBool(EditingEnabledKey, EditingEnabled);
                        EditorUtility.SetDirty(target);
                    }
                    EditorGUI.EndDisabledGroup();
                }

                EditorGUI.EndDisabledGroup();
            }

            serializedObject.ApplyModifiedProperties();
        }