Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

How to send simulated Keyboard events to an InputField from Buttons click handlers

Discussion in 'UGUI & TextMesh Pro' started by jdonzet, Mar 6, 2015.

  1. jdonzet

    jdonzet

    Joined:
    Jun 2, 2012
    Posts:
    4
    I am programming an ingame keyboard to be used in windows standalone. I want to use an InputField as the target for the user input. The problem is the InputField lost focus when a button is clicked (in this case the button represents a keyboard key) and it does´t catch correctly the key that i send.

    To simulate keyboard key pressed events I use this:

    inputField.ProcessEvent(Event.KeyboardEvent("a ")); where "a" represents the a key.

    I tried using inputField.ActivateInputField(); and inputField.Select(); in different ways but it didn't solve my problem.

    Do anyone know how to solve this? Thanks

    Some code

    Code (CSharp):
    1. /* "a" BUTTON CLICK HANDLER    , I also tried putting it inside Update()*/                  
    2. inputField.Select();   // I also tried to use this EventSystem.current.SetSelectedGameObject(go);
    3. inputField.ActivateInputField();
    4. inputField.ProcessEvent(Event.KeyboardEvent("a"));
     
  2. MaeL0000

    MaeL0000

    Joined:
    Aug 8, 2015
    Posts:
    35
    Hi jdonzet, did you manage to get it working?
     
  3. mikecoker

    mikecoker

    Joined:
    Dec 9, 2016
    Posts:
    1
    Just FYI, I was also recently looking into this problem. I was creating an in-game keyboard for use in a VR app for work. Based on what I read, it is not possible to simulate keyboard events that the input box will handle. What you can do (and I did) is basically handle all of the keyboard buttons yourself and then modify the input text value yourself. If you want all of the caret and selection stuff to work, those values (position indices) can be queried from the input box and you can use them to modify your text. It's *almost* as bad as writing your own input box, but not quite that bad.
     
    JoeStrout likes this.
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,848
    Hmph. In my testing, it's pretty much exactly that bad, at least with TMP_InputField. I can get the selectionAnchorPosition and selectionFocusPosition (initially both 0) from the field, and use these to update the text as well as those properties accordingly. But on the next button press, these are both 0 again, despite what I set (and read back) in the last event.

    Even if I explicitly click and drag on the field to make a selection, as soon as I mouse over a button, the field loses focus and forgets its selection. This makes it having a selection entirely useless.

    I haven't tried with the old InputField, and it's remotely possible there is something wonky in my event setup (I'm using a Curved UI asset with a custom ray, as I'm also in VR). But as it is, I'm better off using a straight text rather than an input field, and treat the cursor as always being at the end. :(
     
  5. iabulko

    iabulko

    Joined:
    May 6, 2014
    Posts:
    6
    Hey, if anyone is working with it and still trying to figure it out I think i got it:

    inputField.ProcessEvent(Event.KeyboardEvent("a"));
    inputField.ForceLabelUpdate();

    Works for me ;)
     
    Moran_Comunix and stepkka like this.
  6. dvalles

    dvalles

    Joined:
    Nov 15, 2014
    Posts:
    11
    You're a freaking genius and I love you.
     
  7. Rib

    Rib

    Joined:
    Nov 7, 2013
    Posts:
    39
    After struggling with the same issue supporting a virtual keyboard in VR, it was really helpful to realise from this thread that it's possible to construct fake keyboard Events via
    Event.KeyboardEvent()
    .

    Some limitations I then found were that KeyboardEvent() has some magic syntax for modifiers which interferes with typing some characters ("%", "^", "&", "#") and if you pass an uppercase character then it will be normalized into its lowercase equivalent if you don't specify a Shift or CapsLock modifier.

    In case it's useful for others this is roughly the Component/code I ended up with to at least support basic ascii character input via a virtual keyboard:

    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEngine.UI;
    4. using UnityEngine;
    5.  
    6. public class RFInputField : MonoBehaviour
    7. {
    8.     public TMPro.TMP_InputField inputField;
    9.  
    10.     public string Text
    11.     {
    12.         get {
    13.             return inputField.text;
    14.         }
    15.         set {
    16.             inputField.text = value;
    17.         }
    18.     }
    19.  
    20.     public void SendBackspace()
    21.     {
    22.         if (Text.Length > 0)
    23.         {
    24.             inputField.ProcessEvent(Event.KeyboardEvent("backspace"));
    25.             inputField.ForceLabelUpdate();
    26.         }
    27.     }
    28.  
    29.     public void SendKey(string value)
    30.     {
    31.         // Ref: https://docs.unity3d.com/ScriptReference/Event.KeyboardEvent.html
    32.         // Ref: https://docs.unity3d.com/ScriptReference/Event.html
    33.         // Ref: https://docs.unity3d.com/ScriptReference/KeyCode.html
    34.  
    35.         // "The syntax for the key string is a key name (same as in the Input
    36.         //  Manager), optionally prefixed by any number of modifiers:
    37.         //    & = Alternate, ^ = Control, % = Command/Windows key, # = Shift
    38.         //  Examples: &f12 = Alternate + F12, "^[0]" = Control + keypad0."
    39.  
    40.         // Since there's no documented way to quote/escape the modifier
    41.         // characters or easily handle upper/lowercase letter we augment the
    42.         // usage of Event.KeyboardEvent()
    43.  
    44.         Event fakeEvent;
    45.         switch (value)
    46.         {
    47.             case "&":
    48.                 fakeEvent = Event.KeyboardEvent("a");
    49.                 fakeEvent.keyCode = KeyCode.Ampersand;
    50.                 fakeEvent.character = value[0];
    51.                 break;
    52.             case "^":
    53.                 fakeEvent = Event.KeyboardEvent("a");
    54.                 fakeEvent.keyCode = KeyCode.Caret;
    55.                 fakeEvent.character = value[0];
    56.                 break;
    57.             case "%":
    58.                 fakeEvent = Event.KeyboardEvent("a");
    59.                 fakeEvent.keyCode = KeyCode.Percent;
    60.                 fakeEvent.character = value[0];
    61.                 break;
    62.             case "#":
    63.                 fakeEvent = Event.KeyboardEvent("a");
    64.                 fakeEvent.keyCode = KeyCode.Hash;
    65.                 fakeEvent.character = value[0];
    66.                 break;
    67.             default:
    68.                 if (value.Length != 1)
    69.                 {
    70.                     Debug.LogError("Ignoring spurious multi-character key value: " + value);
    71.                     return;
    72.                 }
    73.                 fakeEvent = Event.KeyboardEvent(value);
    74.                 char keyChar = value[0];
    75.                 fakeEvent.character = keyChar;
    76.                 if (Char.IsUpper(keyChar))
    77.                 {
    78.                     fakeEvent.modifiers |= EventModifiers.Shift;
    79.                 }
    80.                 break;
    81.         }
    82.         inputField.ProcessEvent(fakeEvent);
    83.         inputField.ForceLabelUpdate();
    84.     }
    85. }
    86.  
     
  8. Moran_Comunix

    Moran_Comunix

    Joined:
    May 2, 2020
    Posts:
    3
    Thanks everyone who helped with the "ProcessEvent" solution!
    Is it supposed to work on mobile devices as well? If so, any trivial reason why it doesn't?

    My homemade keyboard intercepts the keys, but the input fields are not receiving the fake events I'm trying to send to them, when running on an Android device.

    Unity version 2019.2.7f2.

    Thanks!
     
  9. virtualjay

    virtualjay

    Joined:
    Aug 4, 2020
    Posts:
    68
    I'm running into the same issue (Unity 2019.4.22f1) with Android (Oculus Quest). I traced it down to InputField calling
    Code (CSharp):
    1. private bool InPlaceEditing()
    2. {
    3.        return !TouchScreenKeyboard.isSupported || m_TouchKeyboardAllowsInPlaceEditing;
    4. }
    5.  
    m_TouchKeyboardAllowsInPlaceEditing is a private variable that gets its value in InputField's private method
    ActivateInputFieldInternal():
    Code (CSharp):
    1. m_TouchKeyboardAllowsInPlaceEditing = TouchScreenKeyboard.isInPlaceEditingAllowed;
    TouchScreenKeyboard defines:
    Code (CSharp):
    1. public static bool isInPlaceEditingAllowed => false;
    This locks it all up pretty tight, since I can't change any of this code due to it all being UnityEngine.UI assembly stuff. I tried making my own InputField by just copypasting the code, but it trips up on a plethora of calls to UnityEngine.UI internal static classes. No matter which direction I come at it, something stands in the way. I could always just set the text, but then I have to implement all the code for every special key, plus cursor management will be near impossible.

    Any solutions?
     
  10. virtualjay

    virtualjay

    Joined:
    Aug 4, 2020
    Posts:
    68
    As this thread was so helpful, I wanted to share the solution that I used to make the InputField work on the Quest. It probably applies to other mobile devices. This has been tested on 2020.3.20.

    I realized this was the way to solve it after finding the way to make the caret visible in this thread:
    https://forum.unity.com/threads/howto-inputfield-always-show-caret.424556/

    // where Input is a property that holds the InputField. You only need to call this once after
    Code (CSharp):
    1. private void AllowKeyboardInput(InputField input)
    2. {
    3.     #if !UNITY_EDITOR
    4.     var f = input.GetType().GetField("m_TouchKeyboardAllowsInPlaceEditing",
    5.         BindingFlags.NonPublic | BindingFlags.Instance);
    6.     f?.SetValue(input, true);
    7.     #endif
    8. }
    9.  
    It uses the same trick to set the property of the InputField. I found that I only needed to do it once, but you might need to do it every time the field is focused if you have multiple ones.

    If you're using TMP_InputField, you don't have to do this and can just set its shouldHideMobileInput to true. Of course, I had some issues with the caret showing on it, so good luck wi that!
     
  11. Hitnrun76

    Hitnrun76

    Joined:
    Oct 13, 2018
    Posts:
    2
    Hello and thanks @Rib you guided me on the right way.

    Here is my solution :
    Code (CSharp):
    1.  
    2.  
    3. public void InputKey(string key)
    4.     {
    5.         Event keyboardEvent = Event.KeyboardEvent(key);
    6.  
    7.         if (isCapsLock)
    8.         {
    9.             keyboardEvent.modifiers |= EventModifiers.Shift;
    10.             keyboardEvent.character = key.ToUpper()[0];
    11.         }
    12.      
    13.         inputField.ProcessEvent(keyboardEvent);
    14.         inputField.ForceLabelUpdate();
    15.     }
    16.  
    Here my "isCapsLock" is a boolean which can be toggled.
     
  12. Hester74

    Hester74

    Joined:
    Jun 27, 2020
    Posts:
    2
    Thank you!
     
  13. Doomlaser

    Doomlaser

    Joined:
    Dec 9, 2013
    Posts:
    22
    For anyone else struggling with this, well, I'm struggling with it too. I have a TextMeshPro InputField that is supposed to be designed around multilane editing of scripts. But when you press return, it loses focus.

    This is my current hacky workaround. It works most of the time, but not all of the time. If anybody has any clues for how I can fix this to work 100% of the time, let me know.

    In the Update() method:
    Code (CSharp):
    1. if (Input.GetKey(KeyCode.Return) && scriptInputField.gameObject.activeSelf) {
    2.     scriptInputField.ActivateInputField();
    3.  
    4.     scriptInputField.ProcessEvent(UnityEngine.Event.KeyboardEvent("down"));
    5.  
    6.     scriptInputField.ForceLabelUpdate();
    7. }
    EDIT: I accidentally had the m_OnFocusSelectAll flag turned on in the TMP_InputField field. Now my hack is simply:

    Code (csharp):
    1.  
    2.         if (Input.GetKey(KeyCode.Return) && scriptInputField.gameObject.activeSelf) {
    3.             scriptInputField.ActivateInputField();
    4.  
    5.             scriptInputField.ForceLabelUpdate();
    6.         }
    Maybe even that is overkill :)
     
    Last edited: Oct 28, 2023