Search Unity

Custom inspector to capture a single key press

Discussion in 'Immediate Mode GUI (IMGUI)' started by hamberge, Sep 6, 2018.

  1. hamberge

    hamberge

    Joined:
    Aug 30, 2015
    Posts:
    36
    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!
     
    Last edited: Sep 6, 2018
  2. hamberge

    hamberge

    Joined:
    Aug 30, 2015
    Posts:
    36
    Madgvox likes this.
  3. Madgvox

    Madgvox

    Joined:
    Apr 13, 2014
    Posts:
    1,317
    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.
     
  4. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,445
    If you are so inclined, can you share the final "enter a keybinding" inspector widget here? Or are you planning this for an asset?
     
  5. hamberge

    hamberge

    Joined:
    Aug 30, 2015
    Posts:
    36
    Sure, once I get it working I'll post it.
     
  6. hamberge

    hamberge

    Joined:
    Aug 30, 2015
    Posts:
    36
    OK, I think this works. (Sorry, I left the debug code in). This is the code for the custom control:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. public static class InputControls {
    7.  
    8.     public static KeyCode KeyCodeField(Rect controlRect, KeyCode keyCode)
    9.     {
    10.         int controlID = GUIUtility.GetControlID(FocusType.Keyboard);
    11.  
    12.         KeyCode retVal = keyCode;
    13.  
    14.         Event evt = Event.current;
    15.  
    16.         switch(evt.GetTypeForControl(controlID))
    17.         {
    18.             case EventType.Repaint:
    19.                 {
    20.                     GUIStyle style = GUI.skin.GetStyle("TextField");
    21.                     if (style == GUIStyle.none) Debug.Log("GUI Style not found");
    22.                     style.Draw(controlRect, new GUIContent(keyCode.ToString()), controlID);
    23.                     break;
    24.                 }
    25.             case EventType.MouseDown:
    26.                 {
    27.                     if(controlRect.Contains(Event.current.mousePosition) && Event.current.button == 0 && GUIUtility.hotControl == 0)
    28.                     {
    29.                         Debug.Log("mouse down event, control id: " + controlID + ", hot control: " + GUIUtility.hotControl);
    30.                         GUIUtility.hotControl = controlID;
    31.                         GUIUtility.keyboardControl = controlID;
    32.                         evt.Use();
    33.                     }
    34.                     break;
    35.                 }
    36.             case EventType.MouseUp:
    37.                 {                    
    38.                     if(GUIUtility.hotControl == controlID)
    39.                     {
    40.                         GUIUtility.hotControl = 0;
    41.                         evt.Use();
    42.                     }
    43.                     break;
    44.  
    45.                 }
    46.             case EventType.KeyDown:
    47.                 {
    48.                     Debug.Log("key down, control id: " + controlID + ", hot control: " + GUIUtility.hotControl);
    49.                     if(GUIUtility.keyboardControl== controlID)
    50.                     {
    51.                         Debug.Log("hotcontrol");
    52.                         retVal = Event.current.keyCode;
    53.                         GUIUtility.hotControl = 0;
    54.                         GUIUtility.keyboardControl = 0;
    55.                         evt.Use();
    56.                     }
    57.                     break;
    58.                 }
    59.             case EventType.KeyUp:
    60.                 {
    61.  
    62.                     break;
    63.                 }
    64.         }
    65.         return retVal;
    66.     }
    67.  
    68.     public static KeyCode KeyCodeFieldLayout(KeyCode keyCode)
    69.     {
    70.         return KeyCodeField(EditorGUILayout.GetControlRect(), keyCode);
    71.     }
    72.  
    73.  
    74. }
    75.  
    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:

    Code (CSharp):
    1. KeyCode keyCode = yourKeyCode;
    2.  
    3.     public override void OnInspectorGUI()
    4.     {
    5.         keyCode = InputControls.KeyCodeFieldLayout(keyCode);
    6.     }
     
    Last edited: Sep 9, 2018
    SimLeek and halley like this.
  7. hamberge

    hamberge

    Joined:
    Aug 30, 2015
    Posts:
    36
    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:
    https://answers.unity.com/questions/721473/shift-key-down-help.html

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