Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Feedback Feature Request: Control of mobile TouchScreenKeyboard visibility on focus change

Discussion in 'UGUI & TextMesh Pro' started by rainandgames, Jul 26, 2020.

  1. rainandgames

    rainandgames

    Joined:
    Aug 6, 2018
    Posts:
    12
    I posted on the forums about this previously and it was suggested to me that I submit it as a bug fix/feature request. I submitted a bug report and was redirected to the forums once again so here I am with this feature request:

    (On an android device) I would like a way to keep the mobile TouchScreenKeyboard open when focus changes to a different UI element. This would include being able to interact with unity in the visible area while the keyboard remains on screen.

    The current implementation forces the mobile TouchScreenKeyboard to close when its focus is lost. This is not ideal and not consistent with keyboard behavior in most other android apps. This prevents me from creating a smooth experience where, for example, the user can input text which is used to show results of a search. When the user tries to select a result, the input is ignored and the keyboard goes away. The user cannot interact with a scroll view or any buttons while the keyboard is on screen. Almost all android chat interfaces allow users to do this, I would love have this functionality in Unity to give the user a predictable and consistent UI experience.

    Here's a google drive link to a sample project showing this problem using Unity 2020.1.0b13. Just run on an android device and tap the inputfield. When you try to interact with the scroll view or button, the input is ignored and the keyboard hides.

    Thank you!
     
    Last edited: Jul 26, 2020
  2. kbomg

    kbomg

    Joined:
    Jan 14, 2021
    Posts:
    4
    up
     
    CrandellWS likes this.
  3. CrandellWS

    CrandellWS

    Joined:
    Oct 31, 2015
    Posts:
    178
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,596
    I brought this up to the Android team again as this is pretty much the #1 request / issue on Android related to text.
     
  5. seancheno

    seancheno

    Joined:
    Mar 16, 2021
    Posts:
    7
    Thank you for doing so. I am also facing this issue. Without a fix, Unity is literally unable to support a professional chat app or any app that relies on messaging.

    This is quite a severe issue affecting many including Unity's adoption. Please let us know when there is a fix. Until then, we can either create a custom keyboard (which is far from ideal since the majority of users I speak to say they hate custom keyboards and prefer the one that they use daily) or to uncheck the "Hide Mobile Input" option which results in an input system that is so far removed from any professional/respectable app.

    Thank you for your time. Quite literally praying this can get fixed asap or else we are at a loss of what to do.
     
    Ardito92ITA likes this.
  6. rainandgames

    rainandgames

    Joined:
    Aug 6, 2018
    Posts:
    12
    Any update on this?

    I found this hack mentioned elsewhere on the forums that seems like it might help:
    https://github.com/mopsicus/UnityMobileInput

    Gonna give it a shot and report back

    Edit: looks like this doesn't work for TMP Input fields. welp, still waiting for this
     
    Last edited: Apr 29, 2021
  7. kbomg

    kbomg

    Joined:
    Jan 14, 2021
    Posts:
    4
  8. jiraphatK

    jiraphatK

    Joined:
    Sep 29, 2018
    Posts:
    306
    Up, the ScreenKeyboard implementation on Android has been notorious for years. It makes input UX on Android universally bad. Users have been requesting, begging, and scolding for changes for a longggggggggggggggggggg time to no avail.
    • Form filling (for registration and such)
    • Chat behavior/app (need to tap 2 times to send a message)
    • Every UIs that uses more than 2 InputField
    • Every time user needs to interact with game elements or other UI when ScreenKeyboard is shown.
    • Screen Keyboard mess up the game life cycle because OnApplicationFocus got called for no good reason.
    • Basically, I think there is no Android game that uses InputField and does not suffer from the weirdness of ScreenKeyboard. Considering that Unity is most known for its dominance over the mobile market....
    I understand that this is not the native behavior of Android's keyboard but instead of wasting developers' hours every time someone needs to implement something more complex with InputField, why couldn't you make a hack for us? this is not even a feature but the most basic functionality that we expect from the engine, which is best known for its mobile game development.

    I have been circumventing this with paid 3rd party tools but I hope for a better future of Unity where this particularly nasty papercut will be gone.
     
    Last edited: Jun 8, 2021
  9. itsnottme

    itsnottme

    Joined:
    Mar 8, 2017
    Posts:
    129
    Has there been any progress on this, or are we forced to use 3rd party assets?
     
  10. Nyankoooo

    Nyankoooo

    Joined:
    May 21, 2016
    Posts:
    144
    Another bump.
     
    D4RKST4R007 and CrandellWS like this.
  11. D4RKST4R007

    D4RKST4R007

    Joined:
    Apr 2, 2020
    Posts:
    1
    Is there any update on this? Implementing a chat feature and have tried almost everything.
     
  12. CrandellWS

    CrandellWS

    Joined:
    Oct 31, 2015
    Posts:
    178
  13. wethings

    wethings

    Joined:
    Aug 26, 2018
    Posts:
    28
  14. CrandellWS

    CrandellWS

    Joined:
    Oct 31, 2015
    Posts:
    178
    So does that mean at least one someone is working on this?

    Can we get an updated comment about this so we know you guys still have eyes on it?

    Thanks
     
  15. Fotal

    Fotal

    Joined:
    Apr 9, 2019
    Posts:
    38
    Up +1
     
  16. TheVirtualMunk

    TheVirtualMunk

    Joined:
    Sep 6, 2019
    Posts:
    150
  17. TheVirtualMunk

    TheVirtualMunk

    Joined:
    Sep 6, 2019
    Posts:
    150
    Has anyone tried this solution with TMP in newer versions?
     
  18. TheVirtualMunk

    TheVirtualMunk

    Joined:
    Sep 6, 2019
    Posts:
    150
  19. Nyankoooo

    Nyankoooo

    Joined:
    May 21, 2016
    Posts:
    144
    I just tried it, and when setting closeKeyboardOnOutsideTap to false, tapping outside of the keyboard still hides it on 2020.3.26f1.
     
    Ardito92ITA and TheVirtualMunk like this.
  20. TheVirtualMunk

    TheVirtualMunk

    Joined:
    Sep 6, 2019
    Posts:
    150
    Yea this is pretty buggy atm - I just tested and it only works when you click on the input field again (as I think it just reopens it instantly). Anywhere else and it closes - but it does let you click stuff while closing the keyboard. So let's say you have a back button in unity UI, you can now click that and it will actually fire, but the keyboard will still close.

    Edit: Looks like the keyboard is hidden on pointer down.

    Edit 2: Unity 2021.2.8f1
     
  21. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,939
    Which UI system are you using ? IMGUI or UGUI?
     
  22. Nyankoooo

    Nyankoooo

    Joined:
    May 21, 2016
    Posts:
    144
    @Tomas1856 uGUI
     
  23. Nyankoooo

    Nyankoooo

    Joined:
    May 21, 2016
    Posts:
    144
  24. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,939
    We talked internally, and it seems there's a mixup with https://docs.unity3d.com/ScriptReference/TouchScreenKeyboard.Android-closeKeyboardOnOutsideTap.html name.

    What it actually does - it controls to consume touch events or pass touch events through while TouchScreenKeyboard is shown.

    The property will be renamed to consumesTouchEvents to reflect better its functionality.

    In 2022.1, its default value is false, thus TouchScreenKeyboard on Android will behave the same as iOS
    Prior to 2022.1, its default value is true, thus not to break users who rely on legacy TouchScreenKeyboard Android behavior.

    In 2023.x, this property will be deprecated, and finally in 2024.x it will be removed, thus ending TouchScreenKeyboard Android behavior transition to match iOS.

    Finally, for the issue above, to hide or to keep TouchScreenKeyboard when touch events are passed through, is controlled by UGUI system itself, I assume current logic dictates if control looses focus, UGUI tells TouchScreenKeyboard to hide. I am not aware of any properties allow to override this behavior.
     
    M929djie and TheVirtualMunk like this.
  25. DrinkableGames

    DrinkableGames

    Joined:
    Jun 20, 2021
    Posts:
    6
    @Tomas1856 I feel kinda dumb but how do you set closeKeyboardOnOutsideTap to false? It doesn't seem to excist when I type it (2021.1.12f1)
     
  26. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,939
    2021.1 is an unsupported version, try using 2021.2
     
    DrinkableGames likes this.
  27. rainandgames

    rainandgames

    Joined:
    Aug 6, 2018
    Posts:
    12
    Hey there, I appreciate the work being done to support this feature. Thank you! This is a great step towards it, I can now interact with unity while the keyboard is open.

    It's still not perfect though as Nyankoooo and TheVirtualMunk have said. My interaction is limited to a single tap, which forces the keyboard to close. This means, for example, no scrolling through a list of results that are populated as you type. A common desired end result for a feature like this I would say. If there's a way to keep the keyboard open as input is passed through, this would be 100% solved!
     
    TheVirtualMunk likes this.
  28. nareshbishtasus

    nareshbishtasus

    Joined:
    Jun 11, 2018
    Posts:
    36
    Facing the same problem so annoying. Pleas resolve this.
     
  29. takajika

    takajika

    Joined:
    Sep 8, 2022
    Posts:
    3
    I have the same issue. "closeKeyboardOnOutsideTap " does not completely solve the problem.
    I would like a way to keep the mobile TouchScreenKeyboard open while focus is on the inputfield.
     
  30. MrZoroJuro

    MrZoroJuro

    Joined:
    Sep 29, 2022
    Posts:
    3
    I am also facing the same problem and very confused.
    In my app, the operability of this UI is one of the important functions.
    I think Unity is a great service for creating games.
    By all means, I would like to ask you to improve the UI.
    Hey Unity, do you have any plans to fix this?
     
    dnrkckzk2 likes this.
  31. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    520
    Unity mobile keyboard controller bridge is horrible! For more then 6 years you are focused on mobile app development and still can't make is work well with full functionality. We have to use custom native plugins to make it work well. Furthermore, on Android device TouchKeyboard NEVER shows the keyboard height. Wtf?
     
    ebobo225, shivster24 and CrandellWS like this.
  32. M929djie

    M929djie

    Joined:
    Apr 20, 2022
    Posts:
    4
    If I set
    Code (CSharp):
    1. TouchScreenKeyboard.Android.consumesOutsideTouches = true;
    in 2021.3 and tap into the multiline input field, the mobile keyboard shows up on the screen. When I tap again onto the mobile screen, somewhere in the app, the mobile keyboard does not disappear; it stays open. This is great, but... the app does not react on any taps anymore.

    This is a bit unfortunate, as I cannot even move the cursor in the input field to a different position with a touch while the keyboard is open. I need to close the keyboard, and tap again, hoping that my tap is accurate enough to place the cursor accurately.

    Great so far, but still room for improvement, certainly.
     
  33. M929djie

    M929djie

    Joined:
    Apr 20, 2022
    Posts:
    4
    Apologies; typo in my reply above:
    The app does react on taps, but the input field doesn't.
    Sorry; I can't edit the post; get a spam warning.
     
  34. shivster24

    shivster24

    Joined:
    Sep 26, 2022
    Posts:
    1
    I just had to edit the text mesh pro package. It's quite an annoying solution, I'm surprised that we can't have full control of the keyboard easily without having to make our own.. Interesting eh? o_O
     
  35. arenart

    arenart

    Joined:
    Nov 1, 2017
    Posts:
    3
    Hi there,
    I think I found the culprit that prevents the selection if Hide Input Mobile = true with a TMP_InputField and the option TouchScreenKeyboard.Android.consumesOutsideTouches = true.
    In my opinion there is a ! missing in front of m_HideMobileInput on line 1623 from TMP_InputField.
    To solve the problem temporarily, I created a class that overrides TMP_InputField and therefore calls Reflection.
    It's not a long term solution but it do the trick for the moment.

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using TMPro.EditorUtilities;
    4. using TMPro;
    5.  
    6. [CustomEditor(typeof(TMP_TouchInputField))]
    7. public class TMP_TouchInputFieldEditor : TMP_InputFieldEditor
    8. {
    9.     #region Context menu
    10.     [MenuItem("CONTEXT/TMP_InputField/Convert To TMP_TouchInputField", true)]
    11.     private static bool _ConvertToTMP_TouchInputField(MenuCommand command)
    12.     {
    13.        return EditorUtils.CanConvertTo<TMP_TouchInputField>(command.context);
    14.     }
    15.  
    16.     [MenuItem("CONTEXT/TMP_InputField/Convert To TMP_TouchInputField", false)]
    17.     private static void ConvertToTMP_TouchInputField(MenuCommand command)
    18.     {
    19.        EditorUtils.ConvertTo<TMP_TouchInputField>(command.context);
    20.     }
    21.  
    22.     [MenuItem("CONTEXT/TMP_TouchInputField/Convert To TMP_InputField", true)]
    23.     private static bool _ConvertToTMP_InputField(MenuCommand command)
    24.     {
    25.         return EditorUtils.CanConvertTo<TMP_InputField>(command.context);
    26.     }
    27.  
    28.     [MenuItem("CONTEXT/TMP_TouchInputField/Convert To TMP_InputField", false)]
    29.     private static void ConvertToTMP_InputField(MenuCommand command)
    30.     {
    31.         EditorUtils.ConvertTo<TMP_InputField>(command.context);
    32.     }
    33.    #endregion
    34. }
    35.  
    36. public static class EditorUtils
    37.     {
    38.         /// <summary>
    39.         /// Verify whether it can be converted to the specified component.
    40.         /// </summary>
    41.         public static bool CanConvertTo<T>(Object context) where T : MonoBehaviour
    42.         {
    43.             return context && context.GetType() != typeof(T);
    44.         }
    45.  
    46.         /// <summary>
    47.         /// Convert to the specified component.
    48.         /// </summary>
    49.         public static void ConvertTo<T>(Object context) where T : MonoBehaviour
    50.         {
    51.             if (PrefabUtility.IsPartOfVariantPrefab(context))
    52.             {
    53.                 Debug.LogError("Cannot convert a component from a variant prefab!");
    54.                 return;
    55.             }
    56.  
    57.             var target = context as MonoBehaviour;
    58.             var so = new SerializedObject(target);
    59.             so.Update();
    60.  
    61.             var oldEnable = target.enabled;
    62.             target.enabled = false;
    63.  
    64.             // Find MonoScript of the specified component.
    65.             foreach (var script in Resources.FindObjectsOfTypeAll<MonoScript>())
    66.             {
    67.                 if (script.GetClass() != typeof(T))
    68.                     continue;
    69.  
    70.                 // Set 'm_Script' to convert.
    71.                 so.FindProperty("m_Script").objectReferenceValue = script;
    72.                 so.ApplyModifiedProperties();
    73.                 break;
    74.             }
    75.  
    76.             (so.targetObject as MonoBehaviour).enabled = oldEnable;
    77.         }
    78.     }
    79.  
    80.  
    Code (CSharp):
    1. using UnityEngine;
    2. using TMPro;
    3. using UnityEngine.EventSystems;
    4. using System.Reflection;
    5.  
    6.  
    7. /// <summary>
    8. /// TMP_InputField overload class to fix an Android specific bug.
    9. /// [Bug: cannot select a part of the text if m_HideMobileInput = true]
    10. /// This class will disappear when the bug will be fixed by Unity in a future update.
    11. /// </summary>
    12.  
    13. public class TMP_TouchInputField : TMP_InputField
    14. {
    15.     #region Attributes
    16.     private FieldInfo m_ShouldActivateNextUpdateInfo;
    17.     private MethodInfo activateInputFieldInternalInfo;
    18.     private FieldInfo m_SelectionStillActiveInfo;
    19.     private FieldInfo m_PreviouslySelectedObjectInfo;
    20.     private FieldInfo m_KeyDownStartTimeInfo;
    21.     private FieldInfo m_DoubleClickDelayInfo;
    22.     private MethodInfo updateMaskRegionsInfo;
    23.     private MethodInfo inPlaceEditingInfo;
    24.     private MethodInfo isKeyboardUsingEventsInfo;
    25.     private MethodInfo assignPositioningIfNeededInfo;
    26.     private FieldInfo m_ReleaseSelectionInfo;
    27.     private FieldInfo m_WasCanceledInfo;
    28.     private MethodInfo updateStringPositionFromKeyboardInfo;
    29.     private MethodInfo sendOnValueChangedAndUpdateLabelInfo;
    30.     private FieldInfo m_ProcessingEventInfo;
    31.     #endregion
    32.  
    33.     #region Unity methods
    34.     protected override void Awake()
    35.     {
    36.         base.Awake();
    37.  
    38.         m_ShouldActivateNextUpdateInfo = typeof(TMP_InputField).GetField("m_ShouldActivateNextUpdate", BindingFlags.NonPublic | BindingFlags.Instance);
    39.         activateInputFieldInternalInfo = typeof(TMP_InputField).GetMethod("ActivateInputFieldInternal", BindingFlags.NonPublic | BindingFlags.Instance);
    40.         m_SelectionStillActiveInfo = typeof(TMP_InputField).GetField("m_SelectionStillActive", BindingFlags.NonPublic | BindingFlags.Instance);
    41.         m_PreviouslySelectedObjectInfo = typeof(TMP_InputField).GetField("m_PreviouslySelectedObject", BindingFlags.NonPublic | BindingFlags.Instance);
    42.         m_KeyDownStartTimeInfo = typeof(TMP_InputField).GetField("m_KeyDownStartTime", BindingFlags.NonPublic | BindingFlags.Instance);
    43.         m_DoubleClickDelayInfo = typeof(TMP_InputField).GetField("m_DoubleClickDelay", BindingFlags.NonPublic | BindingFlags.Instance);
    44.         updateMaskRegionsInfo = typeof(TMP_InputField).GetMethod("UpdateMaskRegions", BindingFlags.NonPublic | BindingFlags.Instance);
    45.         inPlaceEditingInfo = typeof(TMP_InputField).GetMethod("InPlaceEditing", BindingFlags.NonPublic | BindingFlags.Instance);
    46.         isKeyboardUsingEventsInfo = typeof(TMP_InputField).GetMethod("isKeyboardUsingEvents", BindingFlags.NonPublic | BindingFlags.Instance);
    47.         assignPositioningIfNeededInfo = typeof(TMP_InputField).GetMethod("AssignPositioningIfNeeded", BindingFlags.NonPublic | BindingFlags.Instance);
    48.         m_ReleaseSelectionInfo = typeof(TMP_InputField).GetField("m_ReleaseSelection", BindingFlags.NonPublic | BindingFlags.Instance);
    49.         m_WasCanceledInfo = typeof(TMP_InputField).GetField("m_WasCanceled", BindingFlags.NonPublic | BindingFlags.Instance);
    50.         updateStringPositionFromKeyboardInfo = typeof(TMP_InputField).GetMethod("UpdateStringPositionFromKeyboard", BindingFlags.NonPublic | BindingFlags.Instance);
    51.         sendOnValueChangedAndUpdateLabelInfo = typeof(TMP_InputField).GetMethod("SendOnValueChangedAndUpdateLabel", BindingFlags.NonPublic | BindingFlags.Instance);
    52.         m_ProcessingEventInfo = typeof(TMP_InputField).GetField("m_ProcessingEvent", BindingFlags.NonPublic | BindingFlags.Instance);
    53.     }
    54.  
    55.     protected override void LateUpdate()
    56.     {
    57.         if (!Application.isPlaying || Application.platform != RuntimePlatform.Android)
    58.         {
    59.             base.LateUpdate();
    60.             return;
    61.         }
    62.  
    63.         // Only activate if we are not already activated.
    64.         if ((bool)m_ShouldActivateNextUpdateInfo.GetValue(this)) // => if (m_ShouldActivateNextUpdate)
    65.         {
    66.             if (!isFocused)
    67.             {
    68.                 activateInputFieldInternalInfo.Invoke(this, null); // => ActivateInputFieldInternal();
    69.                 m_ShouldActivateNextUpdateInfo.SetValue(this, false); // => m_ShouldActivateNextUpdate = false;
    70.                 return;
    71.             }
    72.  
    73.             // Reset as we are already activated.
    74.             m_ShouldActivateNextUpdateInfo.SetValue(this, false); // => m_ShouldActivateNextUpdate = false;
    75.         }
    76.  
    77.         // Handle double click to reset / deselect Input Field when ResetOnActivation is false.
    78.         if (!isFocused && (bool)m_SelectionStillActiveInfo.GetValue(this)) // => if (!isFocused && m_SelectionStillActive)
    79.         {
    80.             GameObject selectedObject = EventSystem.current != null ? EventSystem.current.currentSelectedGameObject : null;
    81.  
    82.             if (selectedObject == null && m_ResetOnDeActivation)
    83.             {
    84.                 ReleaseSelection();
    85.                 return;
    86.             }
    87.  
    88.             if (selectedObject != null && selectedObject != this.gameObject)
    89.             {
    90.                 if (selectedObject == (GameObject)m_PreviouslySelectedObjectInfo.GetValue(this)) // => if (selectedObject == m_PreviouslySelectedObject)
    91.                     return;
    92.  
    93.                 m_PreviouslySelectedObjectInfo.SetValue(this, selectedObject); // => m_PreviouslySelectedObject = selectedObject;
    94.  
    95.                 // Special handling for Vertical Scrollbar
    96.                 if (m_VerticalScrollbar && selectedObject == m_VerticalScrollbar.gameObject)
    97.                 {
    98.                     // Do not release selection
    99.                     return;
    100.                 }
    101.  
    102.                 // Release selection for all objects when ResetOnDeActivation is true
    103.                 if (m_ResetOnDeActivation)
    104.                 {
    105.                     ReleaseSelection();
    106.                     return;
    107.                 }
    108.  
    109.                 // Release current selection of selected object is another Input Field
    110.                 if (selectedObject.GetComponent<TMP_InputField>() != null)
    111.                     ReleaseSelection();
    112.  
    113.                 return;
    114.             }
    115. #if ENABLE_INPUT_SYSTEM
    116.                 Event processingEvent = (Event)m_ProcessingEventInfo.GetValue(this);
    117.  
    118.                 //if (m_ProcessingEvent != null && m_ProcessingEvent.rawType == EventType.MouseDown && m_ProcessingEvent.button == 0)
    119.                 if (processingEvent != null && processingEvent.rawType == EventType.MouseDown && processingEvent.button == 0)
    120.                 {
    121.                     // Check for Double Click
    122.                     bool isDoubleClick = false;
    123.                     float timeStamp = Time.unscaledTime;
    124.  
    125.                     if ((float)m_KeyDownStartTimeInfo.GetValue(this) + (float)m_DoubleClickDelayInfo.GetValue(this) > timeStamp) // => if (m_KeyDownStartTime + m_DoubleClickDelay > timeStamp)
    126.                         isDoubleClick = true;
    127.  
    128.                     m_KeyDownStartTimeInfo.SetValue(this, timeStamp); // => m_KeyDownStartTime = timeStamp;
    129.  
    130.                     if (isDoubleClick)
    131.                     {
    132.                         //m_StringPosition = m_StringSelectPosition = 0;
    133.                         //m_CaretPosition = m_CaretSelectPosition = 0;
    134.                         //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
    135.  
    136.                         //if (caretRectTrans != null)
    137.                         //    caretRectTrans.localPosition = Vector3.zero;
    138.  
    139.                         ReleaseSelection();
    140.  
    141.                         return;
    142.                     }
    143.                 }
    144. #else
    145.             if (Input.GetKeyDown(KeyCode.Mouse0))
    146.             {
    147.                 // Check for Double Click
    148.                 bool isDoubleClick = false;
    149.                 float timeStamp = Time.unscaledTime;
    150.  
    151.                 if ((float)m_KeyDownStartTimeInfo.GetValue(this) + (float)m_DoubleClickDelayInfo.GetValue(this) > timeStamp) // => if (m_KeyDownStartTime + m_DoubleClickDelay > timeStamp)
    152.                     isDoubleClick = true;
    153.  
    154.                 m_KeyDownStartTimeInfo.SetValue(this, timeStamp); // => m_KeyDownStartTime = timeStamp;
    155.  
    156.                 if (isDoubleClick)
    157.                 {
    158.                     //m_StringPosition = m_StringSelectPosition = 0;
    159.                     //m_CaretPosition = m_CaretSelectPosition = 0;
    160.                     //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
    161.  
    162.                     //if (caretRectTrans != null)
    163.                     //    caretRectTrans.localPosition = Vector3.zero;
    164.  
    165.                     ReleaseSelection();
    166.  
    167.                     return;
    168.                 }
    169.             }
    170. #endif
    171.         }
    172.  
    173.         updateMaskRegionsInfo.Invoke(this, null); // => UpdateMaskRegions();
    174.  
    175.         if ((bool)inPlaceEditingInfo.Invoke(this, null) && (bool)isKeyboardUsingEventsInfo.Invoke(this, null) || !isFocused) // => if (InPlaceEditing() && isKeyboardUsingEvents() || !isFocused)
    176.         {
    177.             return;
    178.         }
    179.  
    180.         assignPositioningIfNeededInfo.Invoke(this, null); // => AssignPositioningIfNeeded();
    181.  
    182.         if (m_SoftKeyboard == null || m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
    183.         {
    184.             if (m_SoftKeyboard != null)
    185.             {
    186.                 if (!readOnly)
    187.                     text = m_SoftKeyboard.text;
    188.  
    189.                 if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.LostFocus)
    190.                     SendTouchScreenKeyboardStatusChanged();
    191.  
    192.                 if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
    193.                 {
    194.                     m_ReleaseSelectionInfo.SetValue(this, true); // => m_ReleaseSelection = true;
    195.                     m_WasCanceledInfo.SetValue(this, true); // => m_WasCanceled = true;
    196.                     SendTouchScreenKeyboardStatusChanged();
    197.                 }
    198.  
    199.                 if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Done)
    200.                 {
    201.                     m_ReleaseSelectionInfo.SetValue(this, true); // => m_ReleaseSelection = true;
    202.                     OnSubmit(null);
    203.                     SendTouchScreenKeyboardStatusChanged();
    204.                 }
    205.             }
    206.  
    207.             OnDeselect(null);
    208.             return;
    209.         }
    210.  
    211.         string val = m_SoftKeyboard.text;
    212.  
    213.         if (m_Text != val)
    214.         {
    215.             if (readOnly) // => if (m_ReadOnly)
    216.             {
    217.                 m_SoftKeyboard.text = m_Text;
    218.             }
    219.             else
    220.             {
    221.                 m_Text = "";
    222.  
    223.                 for (int i = 0; i < val.Length; ++i)
    224.                 {
    225.                     char c = val[i];
    226.  
    227.                     if (c == '\r' || c == 3)
    228.                         c = '\n';
    229.  
    230.                     if (onValidateInput != null)
    231.                         c = onValidateInput(m_Text, m_Text.Length, c);
    232.                     else if (characterValidation != CharacterValidation.None)
    233.                         c = Validate(m_Text, m_Text.Length, c);
    234.  
    235.                     if (lineType == LineType.MultiLineSubmit && c == '\n')
    236.                     {
    237.                         m_SoftKeyboard.text = m_Text;
    238.  
    239.                         OnSubmit(null);
    240.                         OnDeselect(null);
    241.                         return;
    242.                     }
    243.  
    244.                     if (c != 0)
    245.                         m_Text += c;
    246.                 }
    247.  
    248.                 if (characterLimit > 0 && m_Text.Length > characterLimit)
    249.                     m_Text = m_Text.Substring(0, characterLimit);
    250.  
    251.                 updateStringPositionFromKeyboardInfo.Invoke(this, null); // => UpdateStringPositionFromKeyboard();
    252.  
    253.                 // Set keyboard text before updating label, as we might have changed it with validation
    254.                 // and update label will take the old value from keyboard if we don't change it here
    255.                 if (m_Text != val)
    256.                     m_SoftKeyboard.text = m_Text;
    257.  
    258.                 sendOnValueChangedAndUpdateLabelInfo.Invoke(this, null); // => SendOnValueChangedAndUpdateLabel();
    259.             }
    260.         }
    261.         else if (!shouldHideMobileInput && Application.platform == RuntimePlatform.Android)
    262.         {
    263.             updateStringPositionFromKeyboardInfo.Invoke(this, null); // => UpdateStringPositionFromKeyboard();
    264.         }
    265.  
    266.         //else if (m_HideMobileInput) // m_Keyboard.canSetSelection
    267.         //{
    268.         //    int length = stringPositionInternal < stringSelectPositionInternal ? stringSelectPositionInternal - stringPositionInternal : stringPositionInternal - stringSelectPositionInternal;
    269.         //    m_SoftKeyboard.selection = new RangeInt(stringPositionInternal < stringSelectPositionInternal ? stringPositionInternal : stringSelectPositionInternal, length);
    270.         //}
    271.         //else if (!m_HideMobileInput) // m_Keyboard.canGetSelection)
    272.         //{
    273.         //    UpdateStringPositionFromKeyboard();
    274.         //}
    275.  
    276.         if (m_SoftKeyboard != null && m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
    277.         {
    278.             if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
    279.                 m_WasCanceledInfo.SetValue(this, true); // => m_WasCanceled = true;
    280.  
    281.             OnDeselect(null);
    282.         }
    283.     }
    284.     #endregion
    285. }
     
    Last edited: Apr 3, 2023
    ArthurMWM and qcwy like this.
  36. Great_Society

    Great_Society

    Joined:
    Mar 20, 2021
    Posts:
    1
    Bump. There is the same problem with a couple of projects.
     
  37. qcwy

    qcwy

    Joined:
    Jan 10, 2015
    Posts:
    1
    Ah, this problem has troubled me for a long time, thank you very much for locating the problem.
    However, this modification will cause the arrow keys, select-all button, etc. in my phone's IME to not work properly.
    It seems that the problem is that functions such as OnDrag() update the selection area, but m_SoftKeyboard.selection is not refreshed synchronously.
    So the better solution may be refreshing the selection area of the soft keyboard synchronously(call the code UpdateSelection() below) after OnDragEnd ,OnPointerDown,
    MouseDragOutsideRect
    , and prevent UpdateStringPositionFromKeyboard when dragging(m_UpdateDrag==true)
    Too many private functions, it seems to be a better idea to fork out to modify(as attached file)
    Code (CSharp):
    1.  void UpdateSelection()
    2.         {
    3.              
    4.             int length = stringPositionInternal < stringSelectPositionInternal ? stringSelectPositionInternal - stringPositionInternal : stringPositionInternal - stringSelectPositionInternal;
    5.             m_SoftKeyboard.selection = new RangeInt(stringPositionInternal < stringSelectPositionInternal ? stringPositionInternal : stringSelectPositionInternal, length);
    6.          
    7.         }
     

    Attached Files:

  38. Briezar

    Briezar

    Joined:
    Jun 14, 2021
    Posts:
    2
    Since the whole TMP_InputField class is open-source, it would be better to just copy the code directly and fiddle however you want instead of relying on reflection.
     
  39. pktony2011

    pktony2011

    Joined:
    Jul 15, 2020
    Posts:
    2
    if anyone is still looking for an answer, try this. This worked for me.

    1. Copy Whole TMP_InputField script, and make a new file.

    2. Delete following codes in DeactivateInputField(bool) method.
    Code (CSharp):
    1. m_SoftKeyboard.active = false;
    2. m_SoftKeyboard = null;
    I would recommend making a new method like this, because you need to close the keyboard manually.
    Code (CSharp):
    1.  
    2. public void ReleaseKeyboard()
    3. {
    4.      if(m_SoftKeyboard == null) return;
    5.  
    6.      m_SoftKeyboard.active = false;
    7.      m_SoftKeyboard = null;
    8. }
    3. For Android set TouchScreenKeyboard.Android.consumesOutsideTouches to false.
    The official document says it's default value is false, but somehow didn't work until i manually set it to false.
    https://docs.unity3d.com/2022.1/Doc...nKeyboard.Android-consumesOutsideTouches.html
     
    Last edited: Dec 27, 2023
    rll likes this.