Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Unity 4.6 UI: How to prevent Deselect(losing focus) in InputField

Discussion in 'UGUI & TextMesh Pro' started by AlexShaft, Oct 5, 2014.

  1. AlexShaft

    AlexShaft

    Joined:
    Mar 5, 2013
    Posts:
    1
    Hi guys, I need to prevent deselect in InputField. For example : I have smiles popup and I want to show it when you got focus on InputField (you see native keyboard and I want to show that popup above keyboard for example) now when I click on popup item, InputField is losing focus and keyboard is hiding, Is it possible to make my popup items unselectable or something like that, so they won't get focus and leave it for InputField?
    P.S. Return focus to InputField is not a point for me. I need to prevent a losing of focus or getting it by popup;)
    Thanks ;)
     
  2. ortin

    ortin

    Joined:
    Jan 13, 2013
    Posts:
    221
    I'll bump this. In conventional UI systems I would use something like "element.SetFocusable(false)" to prevent it from stealing focus when the element is clicked. Is there some way to do it for UI objects?
     
  3. ortin

    ortin

    Joined:
    Jan 13, 2013
    Posts:
    221
    Can anyone @ Unity answer this?
    Only workaround I found is an ugly hack with overriding OnDeselect on InputField which shouldn't lose the focus and then forcing return of the focus back with SetSelectedGameObject.
     
  4. phil-Unity

    phil-Unity

    Unity UI Lead Developer

    Joined:
    Nov 23, 2012
    Posts:
    1,226
    right now there isnt anything in place that will help you with what you want to do. That being said i understand your use case and it fits nicely into somethign i've been thinking about for a while that just hasnt been done.

    I know thats not the answer you were hoping for but hopefully i'll be able to get some time to fix this sort of problem. If you submit a bug it would let us keep track of it.
     
  5. ortin

    ortin

    Joined:
    Jan 13, 2013
    Posts:
    221
    Thanks for the answer, at least now we know it's being looked at :)
    638430
     
  6. Zec

    Zec

    Joined:
    Jan 3, 2012
    Posts:
    10
    I had a similar issue as you ortin. I'm making an ingame console with the new UI system and wanted the input field to be constantly focused while the console is visible. After trying for quite a while, I settled on a temporary solution to use while I wait for a better solution. Posting it here in case others stumble across your post as well while searching for solutions.

    Code (CSharp):
    1. void Update()
    2. {
    3.     if(inputField.isFocused == false)
    4.     {
    5.           EventSystem.current.SetSelectedGameObject(inputField.gameObject, null);
    6.           inputField.OnPointerClick(new PointerEventData(EventSystem.current));
    7.      }
    8. }
    Edit: Realized that I'm not sure if it's fully viable for your problem, you said you didn't want to refocus on it but that it instead should never lose focus. Oh well, better than nothing I suppose? :)
     
    Last edited: Dec 3, 2014
  7. MarcoMeter

    MarcoMeter

    Joined:
    Jun 14, 2013
    Posts:
    69
    @ Zec your solution applys an unnormal behavior to my InputField. There is always a new Line added to the text and you can't see what gets written inside the InputField.

    I'm working on a Chat Prototype right now. Lets hope that we'll get such a functionality soon :)
     
  8. chrome-alex

    chrome-alex

    Joined:
    Dec 13, 2012
    Posts:
    19
    this.inputField.ActivateInputField();
     
    nosrepsiht2002 likes this.
  9. ortin

    ortin

    Joined:
    Jan 13, 2013
    Posts:
    221
    Reviving old thread.
    Any news on your "something"? :)
     
  10. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    I want this to.

    My work around is to re-activate the input field after the user has pressed my button.

    However, I don't want the text to hilight - which is standard behaviour after the inputField is re-activated (which doesn't occur until LateUpdate()).

    My work requires me to not patch the InputField, so my solution is to wait until the activation occurs and then move the caret to the end:

    Code (CSharp):
    1.         void ReactivateInputField(InputField inputField)
    2.         {
    3.             if (inputField != null)
    4.             {
    5.                 StartCoroutine(ActivateInputFieldWithoutSelection(inputField));
    6.             }
    7.         }
    8.  
    9.         IEnumerator ActivateInputFieldWithoutSelection(InputField inputField)
    10.         {
    11.             inputField.ActivateInputField();
    12.             // wait for the activation to occur in a lateupdate
    13.             yield return new WaitForEndOfFrame();
    14.             // make sure we're still the active ui
    15.             if (EventSystem.current.currentSelectedGameObject == inputField.gameObject)
    16.             {
    17.                 // To remove hilight we'll just show the caret at the end of the line
    18.                 inputField.MoveTextEnd(false);
    19.             }
    20.         }
    However, if you are happy to patch InputField, you should be able to override it, and run code after the LateUpdate().
     
    Saym_C5 likes this.
  11. Saym_C5

    Saym_C5

    Joined:
    Oct 7, 2015
    Posts:
    2
    @peterept
    code works but caret is not blinking any more. Any idea ?
     
  12. Saym_C5

    Saym_C5

    Joined:
    Oct 7, 2015
    Posts:
    2
    looks like setting inputField.caretPosition forces it to blink too :)

    Code (CSharp):
    1. IEnumerator ActivateInputFieldWithoutSelection(InputField inputField)
    2.     {
    3.         yield return null;
    4.         inputField.ActivateInputField();
    5.  
    6.        
    7.         if (EventSystem.current.currentSelectedGameObject == inputField.gameObject)
    8.         {
    9.            // i am using StringBuilder inputBuffer to manually get and set text on inputField
    10.             inputField.caretPosition = inputBuffer.ToString().Length;
    11.        
    12.         }
    13.  
    14.     }
     
  13. idelder

    idelder

    Joined:
    Aug 13, 2017
    Posts:
    1
    I know this is pretty late and not exactly what you were doing but I managed to get it (more or less) working. Here's how I'm typing into input fields using my VR keyboard.

    Build your keyboard out of buttons. Build a second set of buttons for shifted key presses. Child one set to "LowerCaseKeys" and the other to "UpperCaseKeys" game objects. The script for the shift button is at the bottom but obviously you just toggle the game objects.

    Place the following script on all your buttons (except the shift key) and name the game object of the button the string code for that key press, from here: https://answers.unity.com/questions/762073/c-list-of-string-name-for-inputgetkeystring-name.html
    Check "capitalized" on all capital letter keys.
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4.  
    5. public class VRKeyboardKey: MonoBehaviour
    6. {
    7.     [SerializeField] private bool capitalized;
    8.  
    9.     private void Start()
    10.     {
    11.         GetComponent<Button>().onClick.AddListener(() => Press());
    12.     }
    13.  
    14.     public void Press()
    15.     {
    16.         VRInputField input = VRInputField.lastSelected;
    17.  
    18.         if (input == null) return;
    19.      
    20.         if (input.edited == false)
    21.         {
    22.             input.caretPosition = input.caretPos;
    23.             input.selectionAnchorPosition = input.selectionAnchorPos;
    24.         }
    25.  
    26.         Event keyPress = Event.KeyboardEvent(transform.name);
    27.  
    28.         if (capitalized) keyPress.character = char.ToUpper(keyPress.character);
    29.  
    30.         input.ProcessEvent(keyPress);
    31.          
    32.         input.ForceLabelUpdate();
    33.  
    34.         if (input.edited == false)
    35.         {
    36.             input.edited = true;
    37.             input.caretPos = input.selectionAnchorPos;
    38.             input.selectionAnchorPos = -1;
    39.         }
    40.     }
    41. }
    42.  
    Then replace all the relevant input fields with this derived class.
    Code (CSharp):
    1.  
    2. public class VRInputField : UnityEngine.UI.InputField
    3. {
    4.     public int caretPos;
    5.     public int selectionAnchorPos;
    6.     public bool edited;
    7.  
    8.     public static VRInputField lastSelected;
    9.  
    10.     // Update is called once per frame
    11.     void Update()
    12.     {
    13.         if (isFocused)
    14.         {
    15.             selectionAnchorPos = selectionAnchorPosition;
    16.             caretPos = caretPosition;
    17.             edited = false;
    18.  
    19.             lastSelected = this;
    20.         }
    21.     }
    22. }
    23.  
    As for capital letters, here's my shift key script for toggling the keyboard button sets:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4.  
    5. public class ShiftKey : MonoBehaviour
    6. {
    7.     private bool shiftOn;
    8.  
    9.     [SerializeField] private GameObject lowerCaseKeys;
    10.     [SerializeField] private GameObject upperCaseKeys;
    11.  
    12.     private void Start()
    13.     {
    14.         GetComponent<Button>().onClick.AddListener(() => ToggleShiftKeys());
    15.     }
    16.  
    17.     public void ToggleShiftKeys()
    18.     {
    19.         shiftOn = !shiftOn;
    20.  
    21.         lowerCaseKeys.SetActive(!shiftOn);
    22.         upperCaseKeys.SetActive(shiftOn);
    23.     }
    24. }
    The caret wont show while you're typing but otherwise this works pretty well and is surprisingly simple. If someone can work out the caret please let me know.
     
    Nazard and LlGHTER like this.
  14. kergalert

    kergalert

    Joined:
    Feb 1, 2019
    Posts:
    3
    Super-necro but I solved this by extending the relevant UI class and this is a top google result. Here's the snippet for inputfields on iOS at least:


    Code (CSharp):
    1. using UnityEngine.EventSystems;
    2. using UnityEngine.UI;
    3.  
    4. public class PermanentInput : InputField
    5. {
    6.     public override void OnDeselect(BaseEventData eventData)
    7.     {
    8.         //I don't think is necessary but I'm too lazy to rebuild to ios to check. You can probably just return.
    9.         this.OnSelect(eventData);
    10.     }
    11. }
     
  15. tomkail_betterup

    tomkail_betterup

    Joined:
    Nov 17, 2021
    Posts:
    106
    Christ it kills me that this is still unsolved. Adding my "solution", since this thread is still current on google.

    Here's a script with lots of functions for saving and restoring the selected input field (+text mesh pro input), including the carat/selection state, using much of the wisdom presented here. Activates in the same frame, removing the single frame visual glitch where the keyboard starts hiding before it comes back again.

    User is in charge of handling when the state should be saved and restored, since it's often a bit situational - although if you add the script as a component in your scene it'll keep track of the last selected input field state.

    In my case, I save the state when the user first selects an input field, and some buttons in my app force it to be restored. Note that my buttons needed to switch to OnDown rather than OnClick to make this work nicely (although you could just restore on both events instead)

    https://gist.github.com/tomkail/152cb3aca9ddf3942d97d7070c8c8518
     
    Last edited: Jan 5, 2024
  16. JohnnyConnor

    JohnnyConnor

    Joined:
    Aug 10, 2020
    Posts:
    42
    Wow, it's been almost a decade and there's still no official solution. I've come up with 2 solutions for this: the quick one and the longer one.

    1. The quick solution:
    Uses the IPointerClickHandler interface to reestablish focus to a fallback selectable when it's unintentionally deselected.

    This component:
    - Requires a reference to the selectable you want to keep the focus on.
    - Doesn't prevent your fallback selectable from losing focus like OP wanted, it just instantly reestablishes it when it's lost.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3. using UnityEngine.UI;
    4.  
    5. public class UnfocusableSelectable : MonoBehaviour, IPointerClickHandler
    6. {
    7.     [Tooltip("The Selectable that will retrieve the focus when this one is clicked.")]
    8.     [SerializeField] private Selectable _focusFallbackSelectable;
    9.  
    10.     public void OnPointerClick(PointerEventData eventData) =>
    11.         eventData.selectedObject = _focusFallbackSelectable.gameObject
    12.     ;
    13. }
    14.  


    2. The longer solution:

    Involves using an image, colors, interfaces and events to create an interactable that operates outside the UI navigation system.

    This component:
    - Is highly extensible.
    - Makes your interactables truly unfocusable.

    Here's a version that should serve most cases:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.EventSystems;
    4. using UnityEngine.UI;
    5.  
    6. [RequireComponent(typeof(Image))]
    7. public class UnfocusableInteractable :
    8.     MonoBehaviour,
    9.     IPointerClickHandler,
    10.     IPointerDownHandler,
    11.     IPointerEnterHandler,
    12.     IPointerExitHandler,
    13.     IPointerUpHandler
    14. {
    15.     // Variables.
    16.     private Image _interactableImage;
    17.     private bool _interactable = true;
    18.  
    19.  
    20.     // Properties.
    21.     public bool Interactable
    22.     {
    23.         get => _interactable;
    24.         set
    25.         {
    26.             _interactableImage.color = value ?
    27.                 ColorBlock.defaultColorBlock.normalColor :
    28.                 ColorBlock.defaultColorBlock.disabledColor
    29.             ;
    30.             _interactable = value;
    31.         }
    32.     }
    33.  
    34.  
    35.     // Events.
    36.     public Action OnClick;
    37.  
    38.  
    39.     // MonoBehaviour.
    40.     private void Awake() => _interactableImage = GetComponent<Image>();
    41.  
    42.  
    43.     // Interface Methods.
    44.     public void OnPointerClick(PointerEventData eventData)
    45.     {
    46.         if (Interactable) OnClick?.Invoke();
    47.     }
    48.  
    49.     public void OnPointerDown(PointerEventData eventData)
    50.     {
    51.         if (Interactable) _interactableImage.color = ColorBlock.defaultColorBlock.pressedColor;
    52.     }
    53.  
    54.     public void OnPointerEnter(PointerEventData eventData)
    55.     {
    56.         if (Interactable) _interactableImage.color = ColorBlock.defaultColorBlock.highlightedColor;
    57.     }
    58.  
    59.     public void OnPointerExit(PointerEventData eventData)
    60.     {
    61.         if (Interactable) _interactableImage.color = ColorBlock.defaultColorBlock.normalColor;
    62.     }
    63.  
    64.     public void OnPointerUp(PointerEventData eventData)
    65.     {
    66.         if (Interactable) _interactableImage.color = ColorBlock.defaultColorBlock.selectedColor;
    67.     }
    68. }
    69.