Reputation: 33
I want to know if there is a way to only open the keyboard after the click OnMouseUp() not on the OnMouseDown() method. I have tried to modify the inputfield.cs script but it does not open.
Upvotes: 1
Views: 2026
Reputation: 125275
It's possible but tricky and complicated especially when new to Unity.
Do the following:
1. From the Editor disable interactable
on the InputFields you want to only open keyboard when key is released. On the "Disabled Color" of the InputField, make sure to change that to white and the alpha to full (255) so that the InputField will not look like it is disabled.
You don't have to do the remaining steps below because it's already done in the script at the end of this post but the steps below shows you how this is done.
2. When the InputField
interactable is disabled, it cannot accept clicks anymore. Clicking on it will not open the keyboard.
You now have to use GraphicRaycaster.Raycast
to do a manual UI raycast. The result will be stored in a List
of RaycastResult
.
3. Since you want to open the keyboard only when the click or touch is released, put the GraphicRaycaster.Raycast
inside if (Input.GetKeyUp(KeyCode.Mouse0))
for desktop devices and if(Input.GetTouch(0).phase == TouchPhase.Ended)
for mobile devices. They will both return true
on the frame when click or touch is released.
4. When the click is released with the example in #3, check if InputField
is in the List
of the RaycastResult
with result.GetComponentInParent<InputField>()
, if it is an InputField
, manually select the InputField
with the SetSelectedGameObject
and OnPointerClick
functions. After this, the keyboard should open on the InputField
and that's when the key is released not when pressed.
After this, register to the onEndEdit
event on the InputField
.
5. When the onEndEdit
event is called, de-select the InputField
with the DeactivateInputField
function, un-register from the onEndEdit
event with the RemoveAllListeners
function then disable or set interactable back to false
.
Do step #1 only for all the InputField
you want to only open keyboard when click or touch is released then, create a script named "ReleaseInputOpenKeyboard
", copy the script below into it then attach it to an empty GameObject in the scene. The mobile-keyboards will only show on the InputField
when the click or touch is released instead of when it is pressed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class ReleaseInputOpenKeyboard: MonoBehaviour
{
GraphicRaycaster m_Raycaster;
EventSystem m_EventSystem;
List<GameObject> inputFieldProcessedThisFrame = new List<GameObject>();
void Start()
{
m_Raycaster = GameObject.Find("Canvas").GetComponent<GraphicRaycaster>();
//Fetch the Event System from the Scene
m_EventSystem = FindObjectOfType<EventSystem>();
}
void Update()
{
#if UNITY_STANDALONE || UNITY_EDITOR
//DESKTOP COMPUTERS
if (Input.GetKeyUp(KeyCode.Mouse0))
{
SelectInputField();
}
#else
if ((Input.touchCount > 0) && (Input.GetTouch(0).phase == TouchPhase.Ended))
{
SelectInputField();
}
#endif
}
void SelectInputField()
{
//Set up the new Pointer Event
PointerEventData m_PointerEventData = new PointerEventData(m_EventSystem);
//Set the Pointer Event Position to that of the mouse position
m_PointerEventData.position = Input.mousePosition;
//Create a list of Raycast Results
List<RaycastResult> results = new List<RaycastResult>();
//Raycast using the Graphics Raycaster and mouse click position
m_Raycaster.Raycast(m_PointerEventData, results);
//For every result returned, output the name of the GameObject on the Canvas hit by the Ray
foreach (RaycastResult result in results)
{
InputField ipf = result.gameObject.GetComponentInParent<InputField>();
//Check if object hit is an InputField and make sure we have not processed this object this frame
if (ipf && !inputFieldProcessedThisFrame.Contains(ipf.gameObject))
{
Debug.Log("Hit " + ipf.gameObject.name);
//Mark this InputField as processed
inputFieldProcessedThisFrame.Add(ipf.gameObject);
//Enable interactable
ipf.interactable = true;
//Focus on the InputField
ActivateInputField(ipf, m_EventSystem);
//Add event to detect when the Input is deselected
ipf.onEndEdit.AddListener(delegate { OnInputEnd(ipf); });
}
}
//Reset processed InputField
if (inputFieldProcessedThisFrame.Count > 0)
inputFieldProcessedThisFrame.Clear();
}
void ActivateInputField(InputField ipf, EventSystem evSys)
{
evSys.SetSelectedGameObject(ipf.gameObject, new BaseEventData(evSys));
ipf.OnPointerClick(new PointerEventData(evSys));
}
//Detects when InputField is deselected then disable interactible
void OnInputEnd(InputField ipf)
{
StartCoroutine(OnInputEndCOR(ipf));
}
IEnumerator OnInputEndCOR(InputField ipf)
{
//Disable interactable then remove event
ipf.DeactivateInputField();
ipf.onEndEdit.RemoveAllListeners();
/*Wait until EventSystem is no longer in selecting mode
This prevents the "Attempting to select while already selecting an object" error
*/
while (EventSystem.current.alreadySelecting)
yield return null;
ipf.interactable = false;
}
}
Upvotes: 2