Custom inspector to capture a single key press

I want to be able to set in-game key binds in a custom inspector. I am currently doing a non-custom inspector with a public keycode variable showing a dropdown menu of all possible keys. I’d like to make this cleaner. To do this, I’d like to be able to have some kind of field that has the following behavior:

a user clicks it to select it
the user presses a key
the code records the keycode of the key pressed, and then the control is deselected

I can’t seem to figure out how to do this. I know there are text fields that can be used to enter text, but it seems to me that is not the functionality I want.

Can anybody help with this?

Thanks!

Ok… after searching for 2 days I finally found this:

Why is this info not in the manual?

1 Like

The editor is criminally under-documented. Thank you for that find, though. I didn’t think that anyone had taken the time to do a proper primer on the EditorGUI system before.

If you are so inclined, can you share the final “enter a keybinding” inspector widget here? Or are you planning this for an asset?

Sure, once I get it working I’ll post it.

OK, I think this works. (Sorry, I left the debug code in). This is the code for the custom control:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public static class InputControls {

    public static KeyCode KeyCodeField(Rect controlRect, KeyCode keyCode)
    {
        int controlID = GUIUtility.GetControlID(FocusType.Keyboard);

        KeyCode retVal = keyCode;

        Event evt = Event.current;

        switch(evt.GetTypeForControl(controlID))
        {
            case EventType.Repaint:
                {
                    GUIStyle style = GUI.skin.GetStyle("TextField");
                    if (style == GUIStyle.none) Debug.Log("GUI Style not found");
                    style.Draw(controlRect, new GUIContent(keyCode.ToString()), controlID);
                    break;
                }
            case EventType.MouseDown:
                {
                    if(controlRect.Contains(Event.current.mousePosition) && Event.current.button == 0 && GUIUtility.hotControl == 0)
                    {
                        Debug.Log("mouse down event, control id: " + controlID + ", hot control: " + GUIUtility.hotControl);
                        GUIUtility.hotControl = controlID;
                        GUIUtility.keyboardControl = controlID;
                        evt.Use();
                    }
                    break;
                }
            case EventType.MouseUp:
                {                    
                    if(GUIUtility.hotControl == controlID)
                    {
                        GUIUtility.hotControl = 0;
                        evt.Use();
                    }
                    break;

                }
            case EventType.KeyDown:
                {
                    Debug.Log("key down, control id: " + controlID + ", hot control: " + GUIUtility.hotControl);
                    if(GUIUtility.keyboardControl== controlID)
                    {
                        Debug.Log("hotcontrol");
                        retVal = Event.current.keyCode;
                        GUIUtility.hotControl = 0;
                        GUIUtility.keyboardControl = 0;
                        evt.Use();
                    }
                    break;
                }
            case EventType.KeyUp:
                {

                    break;
                }
        }
        return retVal;
    }

    public static KeyCode KeyCodeFieldLayout(KeyCode keyCode)
    {
        return KeyCodeField(EditorGUILayout.GetControlRect(), keyCode);
    }


}

There is a layout version and a non-layout version so you can use this in a custom editor or a property drawer. Usage would be like this:

KeyCode keyCode = yourKeyCode;

    public override void OnInspectorGUI()
    {
        keyCode = InputControls.KeyCodeFieldLayout(keyCode);
    }
2 Likes

I fixed one bug that didn’t allow it to play nicely with other controls in the inspector. Also, this doesn’t capture any “shift” key down. I’m trying to figure out a workaround for that. It might involve designing a custom inspector that uses this control and that provides the option to set keys that are not actually available in the hardware the person using this has. This is probably necessary for cross-platform stuff.

Here is a thread that discusses the shift key issue:

Apparently the shift key issue is a decade-old bug (not kidding!) and still hasn’t been fixed.