CurvedUITMPInputFieldCaret.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. using UnityEngine;
  2. using System.Collections;
  3. using UnityEngine.UI;
  4. using UnityEngine.EventSystems;
  5. #if CURVEDUI_TMP
  6. using TMPro;
  7. #endif
  8. namespace CurvedUI
  9. {
  10. #if !UNITY_5_1 && !UNITY_5_2 && CURVEDUI_TMP
  11. /// <summary>
  12. /// Creates a recttransform caret with image component that can be curved with curvedUI. Hides inputfield's original caret.
  13. /// </summary>
  14. [ExecuteInEditMode]
  15. public class CurvedUITMPInputFieldCaret : MonoBehaviour, ISelectHandler, IDeselectHandler
  16. {
  17. //references
  18. TMP_InputField myField;
  19. RectTransform myCaret;
  20. Color origCaretColor;
  21. Color origSelectionColor;
  22. //variables
  23. bool selected = false;
  24. bool selectingText = false;
  25. void Awake()
  26. {
  27. myField = this.GetComponent<TMP_InputField>();
  28. if (myField)
  29. CheckAndConvertMask();
  30. }
  31. void Update()
  32. {
  33. //only update the caret's position when the field is selected.
  34. if (selected)
  35. UpdateCaret();
  36. }
  37. /// <summary>
  38. /// On select, set the caret active and start blinker coroutine.
  39. /// </summary>
  40. /// <param name="eventData"></param>
  41. public void OnSelect(BaseEventData eventData)
  42. {
  43. if (myCaret == null)
  44. CreateCaret();
  45. selected = true;
  46. myCaret.gameObject.SetActive(true);
  47. StartCoroutine(CaretBlinker());
  48. }
  49. /// <summary>
  50. /// Hide the caret on deselect.
  51. /// </summary>
  52. /// <param name="eventData"></param>
  53. public void OnDeselect(BaseEventData eventData)
  54. {
  55. selected = false;
  56. myCaret.gameObject.SetActive(false);
  57. }
  58. /// <summary>
  59. /// Simple blinker. Blinks the caret if inputfield is selected and user is not selecting text.
  60. /// </summary>
  61. /// <returns></returns>
  62. IEnumerator CaretBlinker()
  63. {
  64. while (selected)
  65. {
  66. myCaret.gameObject.SetActive(selectingText ? true : !myCaret.gameObject.activeSelf);
  67. yield return new WaitForSeconds(0.5f / (float)myField.caretBlinkRate);
  68. }
  69. }
  70. void CreateCaret()
  71. {
  72. //lets create a curvedui caret and copy the settings from our input field
  73. GameObject go = new GameObject("CurvedUI_TMPCaret");
  74. go.AddComponent<RectTransform>();
  75. go.AddComponent<Image>();
  76. go.AddComponent<CurvedUIVertexEffect>();
  77. go.transform.SetParent(transform.GetChild(0).GetChild(0));//Nest the cursor down with the scrolling child so it properly moves with text.
  78. go.transform.localScale = Vector3.one;
  79. (go.transform as RectTransform).anchoredPosition3D = Vector3.zero;
  80. //(go.transform as RectTransform).pivot = new Vector2(0, 0.5f);
  81. (go.transform as RectTransform).pivot = new Vector2(0, 1.0f);
  82. //Copy caret color into new image.
  83. go.GetComponent<Image>().color = myField.caretColor;
  84. myCaret = go.transform as RectTransform;
  85. go.transform.SetAsFirstSibling();
  86. //save original color and hide the original caret
  87. myField.customCaretColor = true;
  88. origCaretColor = myField.caretColor;
  89. myField.caretColor = new Color(0f, 0f, 0f, 0f);
  90. origSelectionColor = myField.selectionColor;
  91. myField.selectionColor = new Color(0f, 0f, 0f, 0f);
  92. go.gameObject.SetActive(false);
  93. }
  94. void UpdateCaret()
  95. {
  96. if (myCaret == null)
  97. CreateCaret();
  98. //Debug.Log("caret:" + myField.caretPosition + " / focus:"+ myField.selectionFocusPosition + " / anchor:" + myField.selectionAnchorPosition + " / charc:" + myField.textComponent.cachedTextGenerator.characterCount + " / charvis:" + myField.textComponent.cachedTextGenerator.characterCountVisible);
  99. Vector2 newCaretPos = GetLocalPositionInText(myField.caretPosition);
  100. //RectTransform originalCaret = (RectTransform)myField.transform.FindChild(myField.name + " Input Caret");
  101. if (myField.selectionFocusPosition != myField.selectionAnchorPosition) // user is selecting text is those two are not equal.
  102. {
  103. selectingText = true;
  104. Vector2 selectionSize = new Vector2(
  105. GetLocalPositionInText(myField.selectionAnchorPosition).x - GetLocalPositionInText(myField.selectionFocusPosition).x,
  106. GetLocalPositionInText(myField.selectionAnchorPosition).y - GetLocalPositionInText(myField.selectionFocusPosition).y
  107. );
  108. newCaretPos = selectionSize.x < 0 ? GetLocalPositionInText(myField.selectionAnchorPosition) : GetLocalPositionInText(myField.selectionFocusPosition);
  109. selectionSize = new Vector2(Mathf.Abs(selectionSize.x), Mathf.Abs(selectionSize.y) + myField.textComponent.fontSize);
  110. myCaret.sizeDelta = new Vector2(selectionSize.x, selectionSize.y);
  111. myCaret.anchoredPosition = newCaretPos;
  112. myCaret.GetComponent<Image>().color = origSelectionColor;
  113. }
  114. else { // user is not selecting text, just update the caret position.
  115. selectingText = false;
  116. //myCaret.sizeDelta = new Vector2(myField.caretWidth, originalCaret == null ? 10 : originalCaret.rect.height);
  117. myCaret.sizeDelta = new Vector2(myField.caretWidth, myField.textComponent.fontSize);
  118. myCaret.anchoredPosition = newCaretPos;
  119. myCaret.GetComponent<Image>().color = origCaretColor;
  120. }
  121. }
  122. /// <summary>
  123. /// Returns the position in TMP_Inputfield's rectransform local space, based on character position in text. Pretty neat actually.
  124. /// </summary>
  125. /// <param name="charNo"></param>
  126. /// <returns></returns>
  127. Vector2 GetLocalPositionInText(int charNo)
  128. {
  129. if (myField.isFocused)
  130. {
  131. TMP_TextInfo txtInfo = myField.textComponent.textInfo;
  132. if (charNo > txtInfo.characterCount - 1) //do not go over the text length.
  133. charNo = txtInfo.characterCount - 1;
  134. TMP_CharacterInfo charInfo = txtInfo.characterInfo[charNo];
  135. return new Vector2(charInfo.topLeft.x, charInfo.ascender);
  136. }
  137. else return Vector2.zero; // field not focused, return 0,0
  138. }
  139. #region MASK CONVERTING
  140. /// <summary>
  141. /// Converts InputField's RectMask2D to a Mask + Image component combination that works better with CurvedUI
  142. /// </summary>
  143. void CheckAndConvertMask()
  144. {
  145. foreach(Transform trans in this.transform)
  146. {
  147. if(trans.GetComponent<RectMask2D>()!= null)
  148. {
  149. DestroyImmediate (trans.GetComponent<RectMask2D>());
  150. trans.AddComponentIfMissing<Image>();
  151. trans.AddComponentIfMissing<Mask>();
  152. }
  153. }
  154. }
  155. #endregion
  156. #region SETTERS AND GETTERS
  157. public Color CaretColor {
  158. get { return origCaretColor; }
  159. set { origCaretColor = value; }
  160. }
  161. public Color SelectionColor {
  162. get { return origSelectionColor; }
  163. set { origSelectionColor = value; }
  164. }
  165. public float CaretBlinkRate {
  166. get { return myField.caretBlinkRate; }
  167. set { myField.caretBlinkRate = value; }
  168. }
  169. #endregion
  170. #else
  171. public class CurvedUITMPInputFieldCaret : MonoBehaviour
  172. {
  173. //Unity before 5.3 does not contain methods necessary to curve the input field caret without changing original script.
  174. #endif
  175. }
  176. }