You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3546 lines
129 KiB

  1. //#define TMP_DEBUG_MODE
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using UnityEngine;
  8. using UnityEngine.UI;
  9. using UnityEngine.Events;
  10. using UnityEngine.EventSystems;
  11. using UnityEngine.Serialization;
  12. namespace TMPro
  13. {
  14. /// <summary>
  15. /// Editable text input field.
  16. /// </summary>
  17. [AddComponentMenu("UI/TextMeshPro - Input Field", 11)]
  18. public class TMP_InputField : Selectable,
  19. IUpdateSelectedHandler,
  20. IBeginDragHandler,
  21. IDragHandler,
  22. IEndDragHandler,
  23. IPointerClickHandler,
  24. ISubmitHandler,
  25. ICanvasElement,
  26. IScrollHandler
  27. {
  28. // Setting the content type acts as a shortcut for setting a combination of InputType, CharacterValidation, LineType, and TouchScreenKeyboardType
  29. public enum ContentType
  30. {
  31. Standard,
  32. Autocorrected,
  33. IntegerNumber,
  34. DecimalNumber,
  35. Alphanumeric,
  36. Name,
  37. EmailAddress,
  38. Password,
  39. Pin,
  40. Custom
  41. }
  42. public enum InputType
  43. {
  44. Standard,
  45. AutoCorrect,
  46. Password,
  47. }
  48. public enum CharacterValidation
  49. {
  50. None,
  51. Digit,
  52. Integer,
  53. Decimal,
  54. Alphanumeric,
  55. Name,
  56. Regex,
  57. EmailAddress,
  58. CustomValidator
  59. }
  60. public enum LineType
  61. {
  62. SingleLine,
  63. MultiLineSubmit,
  64. MultiLineNewline
  65. }
  66. public delegate char OnValidateInput(string text, int charIndex, char addedChar);
  67. [Serializable]
  68. public class SubmitEvent : UnityEvent<string> { }
  69. [Serializable]
  70. public class OnChangeEvent : UnityEvent<string> { }
  71. [Serializable]
  72. public class SelectionEvent : UnityEvent<string> { }
  73. [Serializable]
  74. public class TextSelectionEvent : UnityEvent<string, int, int> { }
  75. protected TouchScreenKeyboard m_Keyboard;
  76. static private readonly char[] kSeparators = { ' ', '.', ',', '\t', '\r', '\n' };
  77. #region Exposed properties
  78. /// <summary>
  79. /// Text Text used to display the input's value.
  80. /// </summary>
  81. [SerializeField]
  82. protected RectTransform m_TextViewport;
  83. //Vector3[] m_ViewportCorners = new Vector3[4];
  84. [SerializeField]
  85. protected TMP_Text m_TextComponent;
  86. protected RectTransform m_TextComponentRectTransform;
  87. [SerializeField]
  88. protected Graphic m_Placeholder;
  89. [SerializeField]
  90. protected Scrollbar m_VerticalScrollbar;
  91. [SerializeField]
  92. protected TMP_ScrollbarEventHandler m_VerticalScrollbarEventHandler;
  93. //private bool m_ForceDeactivation;
  94. /// <summary>
  95. /// Used to keep track of scroll position
  96. /// </summary>
  97. private float m_ScrollPosition;
  98. /// <summary>
  99. ///
  100. /// </summary>
  101. [SerializeField]
  102. protected float m_ScrollSensitivity = 1.0f;
  103. //[SerializeField]
  104. //protected TMP_Text m_PlaceholderTextComponent;
  105. [SerializeField]
  106. private ContentType m_ContentType = ContentType.Standard;
  107. /// <summary>
  108. /// Type of data expected by the input field.
  109. /// </summary>
  110. [SerializeField]
  111. private InputType m_InputType = InputType.Standard;
  112. /// <summary>
  113. /// The character used to hide text in password field.
  114. /// </summary>
  115. [SerializeField]
  116. private char m_AsteriskChar = '*';
  117. /// <summary>
  118. /// Keyboard type applies to mobile keyboards that get shown.
  119. /// </summary>
  120. [SerializeField]
  121. private TouchScreenKeyboardType m_KeyboardType = TouchScreenKeyboardType.Default;
  122. [SerializeField]
  123. private LineType m_LineType = LineType.SingleLine;
  124. /// <summary>
  125. /// Should hide mobile input.
  126. /// </summary>
  127. [SerializeField]
  128. private bool m_HideMobileInput = false;
  129. /// <summary>
  130. /// What kind of validation to use with the input field's data.
  131. /// </summary>
  132. [SerializeField]
  133. private CharacterValidation m_CharacterValidation = CharacterValidation.None;
  134. /// <summary>
  135. /// The Regex expression used for validating the text input.
  136. /// </summary>
  137. [SerializeField]
  138. private string m_RegexValue = string.Empty;
  139. /// <summary>
  140. /// The point sized used by the placeholder and input text object.
  141. /// </summary>
  142. [SerializeField]
  143. private float m_GlobalPointSize = 14;
  144. /// <summary>
  145. /// Maximum number of characters allowed before input no longer works.
  146. /// </summary>
  147. [SerializeField]
  148. private int m_CharacterLimit = 0;
  149. /// <summary>
  150. /// Event delegates triggered when the input field submits its data.
  151. /// </summary>
  152. [SerializeField]
  153. private SubmitEvent m_OnEndEdit = new SubmitEvent();
  154. /// <summary>
  155. /// Event delegates triggered when the input field submits its data.
  156. /// </summary>
  157. [SerializeField]
  158. private SubmitEvent m_OnSubmit = new SubmitEvent();
  159. /// <summary>
  160. /// Event delegates triggered when the input field is focused.
  161. /// </summary>
  162. [SerializeField]
  163. private SelectionEvent m_OnSelect = new SelectionEvent();
  164. /// <summary>
  165. /// Event delegates triggered when the input field focus is lost.
  166. /// </summary>
  167. [SerializeField]
  168. private SelectionEvent m_OnDeselect = new SelectionEvent();
  169. /// <summary>
  170. /// Event delegates triggered when the text is selected / highlighted.
  171. /// </summary>
  172. [SerializeField]
  173. private TextSelectionEvent m_OnTextSelection = new TextSelectionEvent();
  174. /// <summary>
  175. /// Event delegates triggered when text is no longer select / highlighted.
  176. /// </summary>
  177. [SerializeField]
  178. private TextSelectionEvent m_OnEndTextSelection = new TextSelectionEvent();
  179. /// <summary>
  180. /// Event delegates triggered when the input field changes its data.
  181. /// </summary>
  182. [SerializeField]
  183. private OnChangeEvent m_OnValueChanged = new OnChangeEvent();
  184. /// <summary>
  185. /// Custom validation callback.
  186. /// </summary>
  187. [SerializeField]
  188. private OnValidateInput m_OnValidateInput;
  189. [SerializeField]
  190. private Color m_CaretColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
  191. [SerializeField]
  192. private bool m_CustomCaretColor = false;
  193. [SerializeField]
  194. private Color m_SelectionColor = new Color(168f / 255f, 206f / 255f, 255f / 255f, 192f / 255f);
  195. /// <summary>
  196. /// Input field's value.
  197. /// </summary>
  198. [SerializeField]
  199. [TextArea(3, 10)]
  200. protected string m_Text = string.Empty;
  201. [SerializeField]
  202. [Range(0f, 4f)]
  203. private float m_CaretBlinkRate = 0.85f;
  204. [SerializeField]
  205. [Range(1, 5)]
  206. private int m_CaretWidth = 1;
  207. [SerializeField]
  208. private bool m_ReadOnly = false;
  209. [SerializeField]
  210. private bool m_RichText = true;
  211. #endregion
  212. protected int m_StringPosition = 0;
  213. protected int m_StringSelectPosition = 0;
  214. protected int m_CaretPosition = 0;
  215. protected int m_CaretSelectPosition = 0;
  216. private RectTransform caretRectTrans = null;
  217. protected UIVertex[] m_CursorVerts = null;
  218. private CanvasRenderer m_CachedInputRenderer;
  219. private Vector2 m_DefaultTransformPosition;
  220. private Vector2 m_LastPosition;
  221. [NonSerialized]
  222. protected Mesh m_Mesh;
  223. private bool m_AllowInput = false;
  224. //bool m_HasLostFocus = false;
  225. private bool m_ShouldActivateNextUpdate = false;
  226. private bool m_UpdateDrag = false;
  227. private bool m_DragPositionOutOfBounds = false;
  228. private const float kHScrollSpeed = 0.05f;
  229. private const float kVScrollSpeed = 0.10f;
  230. protected bool m_CaretVisible;
  231. private Coroutine m_BlinkCoroutine = null;
  232. private float m_BlinkStartTime = 0.0f;
  233. private Coroutine m_DragCoroutine = null;
  234. private string m_OriginalText = "";
  235. private bool m_WasCanceled = false;
  236. private bool m_HasDoneFocusTransition = false;
  237. private bool m_IsScrollbarUpdateRequired = false;
  238. private bool m_IsUpdatingScrollbarValues = false;
  239. private bool m_isLastKeyBackspace = false;
  240. private float m_ClickStartTime;
  241. private float m_DoubleClickDelay = 0.5f;
  242. // Doesn't include dot and @ on purpose! See usage for details.
  243. const string kEmailSpecialCharacters = "!#$%&'*+-/=?^_`{|}~";
  244. protected TMP_InputField()
  245. { }
  246. protected Mesh mesh
  247. {
  248. get
  249. {
  250. if (m_Mesh == null)
  251. m_Mesh = new Mesh();
  252. return m_Mesh;
  253. }
  254. }
  255. /// <summary>
  256. /// Should the mobile keyboard input be hidden.
  257. /// </summary>
  258. public bool shouldHideMobileInput
  259. {
  260. set
  261. {
  262. SetPropertyUtility.SetStruct(ref m_HideMobileInput, value);
  263. }
  264. get
  265. {
  266. switch (Application.platform)
  267. {
  268. case RuntimePlatform.Android:
  269. case RuntimePlatform.IPhonePlayer:
  270. case RuntimePlatform.tvOS:
  271. return m_HideMobileInput;
  272. }
  273. return true;
  274. }
  275. }
  276. /// <summary>
  277. /// Input field's current text value.
  278. /// </summary>
  279. public string text
  280. {
  281. get
  282. {
  283. return m_Text;
  284. }
  285. set
  286. {
  287. if (this.text == value)
  288. return;
  289. if (value == null) value = string.Empty;
  290. m_Text = value;
  291. //if (m_LineType == LineType.SingleLine)
  292. // m_Text = m_Text.Replace("\n", "").Replace("\t", "");
  293. //// If we have an input validator, validate the input and apply the character limit at the same time.
  294. //if (onValidateInput != null || characterValidation != CharacterValidation.None)
  295. //{
  296. // m_Text = "";
  297. // OnValidateInput validatorMethod = onValidateInput ?? Validate;
  298. // m_CaretPosition = m_CaretSelectPosition = value.Length;
  299. // int charactersToCheck = characterLimit > 0 ? Math.Min(characterLimit - 1, value.Length) : value.Length;
  300. // for (int i = 0; i < charactersToCheck; ++i)
  301. // {
  302. // char c = validatorMethod(m_Text, m_Text.Length, value[i]);
  303. // if (c != 0)
  304. // m_Text += c;
  305. // }
  306. //}
  307. //else
  308. //{
  309. // m_Text = characterLimit > 0 && value.Length > characterLimit ? value.Substring(0, characterLimit) : value;
  310. //}
  311. #if UNITY_EDITOR
  312. if (!Application.isPlaying)
  313. {
  314. SendOnValueChangedAndUpdateLabel();
  315. return;
  316. }
  317. #endif
  318. if (m_Keyboard != null)
  319. m_Keyboard.text = m_Text;
  320. if (m_StringPosition > m_Text.Length)
  321. m_StringPosition = m_StringSelectPosition = m_Text.Length;
  322. // Set RectTransform relative position to top of viewport.
  323. AdjustTextPositionRelativeToViewport(0);
  324. m_forceRectTransformAdjustment = true;
  325. SendOnValueChangedAndUpdateLabel();
  326. }
  327. }
  328. public bool isFocused
  329. {
  330. get { return m_AllowInput; }
  331. }
  332. public float caretBlinkRate
  333. {
  334. get { return m_CaretBlinkRate; }
  335. set
  336. {
  337. if (SetPropertyUtility.SetStruct(ref m_CaretBlinkRate, value))
  338. {
  339. if (m_AllowInput)
  340. SetCaretActive();
  341. }
  342. }
  343. }
  344. public int caretWidth { get { return m_CaretWidth; } set { if (SetPropertyUtility.SetStruct(ref m_CaretWidth, value)) MarkGeometryAsDirty(); } }
  345. public RectTransform textViewport { get { return m_TextViewport; } set { SetPropertyUtility.SetClass(ref m_TextViewport, value); } }
  346. public TMP_Text textComponent { get { return m_TextComponent; } set { SetPropertyUtility.SetClass(ref m_TextComponent, value); } }
  347. //public TMP_Text placeholderTextComponent { get { return m_PlaceholderTextComponent; } set { SetPropertyUtility.SetClass(ref m_PlaceholderTextComponent, value); } }
  348. public Graphic placeholder { get { return m_Placeholder; } set { SetPropertyUtility.SetClass(ref m_Placeholder, value); } }
  349. public Scrollbar verticalScrollbar
  350. {
  351. get { return m_VerticalScrollbar; }
  352. set
  353. {
  354. if (m_VerticalScrollbar != null)
  355. m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange);
  356. SetPropertyUtility.SetClass(ref m_VerticalScrollbar, value);
  357. if (m_VerticalScrollbar)
  358. {
  359. m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange);
  360. }
  361. }
  362. }
  363. public float scrollSensitivity { get { return m_ScrollSensitivity; } set { if (SetPropertyUtility.SetStruct(ref m_ScrollSensitivity, value)) MarkGeometryAsDirty(); } }
  364. public Color caretColor { get { return customCaretColor ? m_CaretColor : textComponent.color; } set { if (SetPropertyUtility.SetColor(ref m_CaretColor, value)) MarkGeometryAsDirty(); } }
  365. public bool customCaretColor { get { return m_CustomCaretColor; } set { if (m_CustomCaretColor != value) { m_CustomCaretColor = value; MarkGeometryAsDirty(); } } }
  366. public Color selectionColor { get { return m_SelectionColor; } set { if (SetPropertyUtility.SetColor(ref m_SelectionColor, value)) MarkGeometryAsDirty(); } }
  367. public SubmitEvent onEndEdit { get { return m_OnEndEdit; } set { SetPropertyUtility.SetClass(ref m_OnEndEdit, value); } }
  368. public SubmitEvent onSubmit { get { return m_OnSubmit; } set { SetPropertyUtility.SetClass(ref m_OnSubmit, value); } }
  369. public SelectionEvent onSelect { get { return m_OnSelect; } set { SetPropertyUtility.SetClass(ref m_OnSelect, value); } }
  370. public SelectionEvent onDeselect { get { return m_OnDeselect; } set { SetPropertyUtility.SetClass(ref m_OnDeselect, value); } }
  371. public TextSelectionEvent onTextSelection { get { return m_OnTextSelection; } set { SetPropertyUtility.SetClass(ref m_OnTextSelection, value); } }
  372. public TextSelectionEvent onEndTextSelection { get { return m_OnEndTextSelection; } set { SetPropertyUtility.SetClass(ref m_OnEndTextSelection, value); } }
  373. public OnChangeEvent onValueChanged { get { return m_OnValueChanged; } set { SetPropertyUtility.SetClass(ref m_OnValueChanged, value); } }
  374. public OnValidateInput onValidateInput { get { return m_OnValidateInput; } set { SetPropertyUtility.SetClass(ref m_OnValidateInput, value); } }
  375. public int characterLimit { get { return m_CharacterLimit; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterLimit, Math.Max(0, value))) UpdateLabel(); } }
  376. //public bool isInteractableControl { set { if ( } }
  377. /// <summary>
  378. /// Set the point size on both Placeholder and Input text object.
  379. /// </summary>
  380. public float pointSize
  381. {
  382. get { return m_GlobalPointSize; }
  383. set {
  384. if (SetPropertyUtility.SetStruct(ref m_GlobalPointSize, Math.Max(0, value)))
  385. {
  386. SetGlobalPointSize(m_GlobalPointSize);
  387. UpdateLabel();
  388. }
  389. }
  390. }
  391. /// <summary>
  392. /// Sets the Font Asset on both Placeholder and Input child objects.
  393. /// </summary>
  394. public TMP_FontAsset fontAsset
  395. {
  396. get { return m_GlobalFontAsset; }
  397. set
  398. {
  399. if (SetPropertyUtility.SetClass(ref m_GlobalFontAsset, value))
  400. {
  401. SetGlobalFontAsset(m_GlobalFontAsset);
  402. UpdateLabel();
  403. }
  404. }
  405. }
  406. [SerializeField]
  407. protected TMP_FontAsset m_GlobalFontAsset;
  408. /// <summary>
  409. /// Determines if the whole text will be selected when focused.
  410. /// </summary>
  411. public bool onFocusSelectAll
  412. {
  413. get { return m_OnFocusSelectAll; }
  414. set { m_OnFocusSelectAll = value; }
  415. }
  416. [SerializeField]
  417. protected bool m_OnFocusSelectAll = true;
  418. protected bool m_isSelectAll;
  419. /// <summary>
  420. /// Determines if the text and caret position as well as selection will be reset when the input field is deactivated.
  421. /// </summary>
  422. public bool resetOnDeActivation
  423. {
  424. get { return m_ResetOnDeActivation; }
  425. set { m_ResetOnDeActivation = value; }
  426. }
  427. [SerializeField]
  428. protected bool m_ResetOnDeActivation = true;
  429. /// <summary>
  430. /// Controls whether the original text is restored when pressing "ESC".
  431. /// </summary>
  432. public bool restoreOriginalTextOnEscape
  433. {
  434. get { return m_RestoreOriginalTextOnEscape; }
  435. set { m_RestoreOriginalTextOnEscape = value; }
  436. }
  437. [SerializeField]
  438. private bool m_RestoreOriginalTextOnEscape = true;
  439. /// <summary>
  440. /// Is Rich Text editing allowed?
  441. /// </summary>
  442. public bool isRichTextEditingAllowed
  443. {
  444. get { return m_isRichTextEditingAllowed; }
  445. set { m_isRichTextEditingAllowed = value; }
  446. }
  447. [SerializeField]
  448. protected bool m_isRichTextEditingAllowed = true;
  449. // Content Type related
  450. public ContentType contentType { get { return m_ContentType; } set { if (SetPropertyUtility.SetStruct(ref m_ContentType, value)) EnforceContentType(); } }
  451. public LineType lineType { get { return m_LineType; } set { if (SetPropertyUtility.SetStruct(ref m_LineType, value)) SetTextComponentWrapMode(); SetToCustomIfContentTypeIsNot(ContentType.Standard, ContentType.Autocorrected); } }
  452. public InputType inputType { get { return m_InputType; } set { if (SetPropertyUtility.SetStruct(ref m_InputType, value)) SetToCustom(); } }
  453. public TouchScreenKeyboardType keyboardType { get { return m_KeyboardType; } set { if (SetPropertyUtility.SetStruct(ref m_KeyboardType, value)) SetToCustom(); } }
  454. public CharacterValidation characterValidation { get { return m_CharacterValidation; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterValidation, value)) SetToCustom(); } }
  455. /// <summary>
  456. /// Sets the Input Validation to use a Custom Input Validation script.
  457. /// </summary>
  458. public TMP_InputValidator inputValidator
  459. {
  460. get { return m_InputValidator; }
  461. set { if (SetPropertyUtility.SetClass(ref m_InputValidator, value)) SetToCustom(CharacterValidation.CustomValidator); }
  462. }
  463. [SerializeField]
  464. protected TMP_InputValidator m_InputValidator = null;
  465. public bool readOnly { get { return m_ReadOnly; } set { m_ReadOnly = value; } }
  466. public bool richText { get { return m_RichText; } set { m_RichText = value; SetTextComponentRichTextMode(); } }
  467. // Derived property
  468. public bool multiLine { get { return m_LineType == LineType.MultiLineNewline || lineType == LineType.MultiLineSubmit; } }
  469. // Not shown in Inspector.
  470. public char asteriskChar { get { return m_AsteriskChar; } set { if (SetPropertyUtility.SetStruct(ref m_AsteriskChar, value)) UpdateLabel(); } }
  471. public bool wasCanceled { get { return m_WasCanceled; } }
  472. protected void ClampStringPos(ref int pos)
  473. {
  474. if (pos < 0)
  475. pos = 0;
  476. else if (pos > text.Length)
  477. pos = text.Length;
  478. }
  479. protected void ClampCaretPos(ref int pos)
  480. {
  481. if (pos < 0)
  482. pos = 0;
  483. else if (pos > m_TextComponent.textInfo.characterCount - 1)
  484. pos = m_TextComponent.textInfo.characterCount - 1;
  485. }
  486. /// <summary>
  487. /// Current position of the cursor.
  488. /// Getters are public Setters are protected
  489. /// </summary>
  490. protected int caretPositionInternal { get { return m_CaretPosition + Input.compositionString.Length; } set { m_CaretPosition = value; ClampCaretPos(ref m_CaretPosition); } }
  491. protected int stringPositionInternal { get { return m_StringPosition + Input.compositionString.Length; } set { m_StringPosition = value; ClampStringPos(ref m_StringPosition); } }
  492. protected int caretSelectPositionInternal { get { return m_CaretSelectPosition + Input.compositionString.Length; } set { m_CaretSelectPosition = value; ClampCaretPos(ref m_CaretSelectPosition); } }
  493. protected int stringSelectPositionInternal { get { return m_StringSelectPosition + Input.compositionString.Length; } set { m_StringSelectPosition = value; ClampStringPos(ref m_StringSelectPosition); } }
  494. private bool hasSelection { get { return stringPositionInternal != stringSelectPositionInternal; } }
  495. private bool m_isSelected;
  496. private bool isStringPositionDirty;
  497. private bool m_forceRectTransformAdjustment;
  498. /// <summary>
  499. /// Get: Returns the focus position as thats the position that moves around even during selection.
  500. /// Set: Set both the anchor and focus position such that a selection doesn't happen
  501. /// </summary>
  502. public int caretPosition
  503. {
  504. get { return caretSelectPositionInternal; }
  505. set { selectionAnchorPosition = value; selectionFocusPosition = value; isStringPositionDirty = true; }
  506. }
  507. /// <summary>
  508. /// Get: Returns the fixed position of selection
  509. /// Set: If Input.compositionString is 0 set the fixed position
  510. /// </summary>
  511. public int selectionAnchorPosition
  512. {
  513. get
  514. {
  515. return caretPositionInternal;
  516. }
  517. set
  518. {
  519. if (Input.compositionString.Length != 0)
  520. return;
  521. caretPositionInternal = value;
  522. isStringPositionDirty = true;
  523. }
  524. }
  525. /// <summary>
  526. /// Get: Returns the variable position of selection
  527. /// Set: If Input.compositionString is 0 set the variable position
  528. /// </summary>
  529. public int selectionFocusPosition
  530. {
  531. get
  532. {
  533. return caretSelectPositionInternal;
  534. }
  535. set
  536. {
  537. if (Input.compositionString.Length != 0)
  538. return;
  539. caretSelectPositionInternal = value;
  540. isStringPositionDirty = true;
  541. }
  542. }
  543. /// <summary>
  544. ///
  545. /// </summary>
  546. public int stringPosition
  547. {
  548. get { return stringSelectPositionInternal; }
  549. set { selectionStringAnchorPosition = value; selectionStringFocusPosition = value; }
  550. }
  551. /// <summary>
  552. /// The fixed position of the selection in the raw string which may contains rich text.
  553. /// </summary>
  554. public int selectionStringAnchorPosition
  555. {
  556. get
  557. {
  558. return stringPositionInternal;
  559. }
  560. set
  561. {
  562. if (Input.compositionString.Length != 0)
  563. return;
  564. stringPositionInternal = value;
  565. //isStringPositionDirty = true;
  566. }
  567. }
  568. /// <summary>
  569. /// The variable position of the selection in the raw string which may contains rich text.
  570. /// </summary>
  571. public int selectionStringFocusPosition
  572. {
  573. get
  574. {
  575. return stringSelectPositionInternal;
  576. }
  577. set
  578. {
  579. if (Input.compositionString.Length != 0)
  580. return;
  581. stringSelectPositionInternal = value;
  582. //isStringPositionDirty = true;
  583. }
  584. }
  585. #if UNITY_EDITOR
  586. // Remember: This is NOT related to text validation!
  587. // This is Unity's own OnValidate method which is invoked when changing values in the Inspector.
  588. protected override void OnValidate()
  589. {
  590. base.OnValidate();
  591. EnforceContentType();
  592. m_CharacterLimit = Math.Max(0, m_CharacterLimit);
  593. //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
  594. if (!IsActive())
  595. return;
  596. SetTextComponentRichTextMode();
  597. UpdateLabel();
  598. if (m_AllowInput)
  599. SetCaretActive();
  600. }
  601. #endif // if UNITY_EDITOR
  602. protected override void OnEnable()
  603. {
  604. //Debug.Log("*** OnEnable() *** - " + this.name);
  605. base.OnEnable();
  606. if (m_Text == null)
  607. m_Text = string.Empty;
  608. if (Application.isPlaying)
  609. {
  610. if (m_CachedInputRenderer == null && m_TextComponent != null)
  611. {
  612. GameObject go = new GameObject(transform.name + " Input Caret", typeof(RectTransform));
  613. // Add MaskableGraphic Component
  614. TMP_SelectionCaret caret = go.AddComponent<TMP_SelectionCaret>();
  615. caret.raycastTarget = false;
  616. caret.color = Color.clear;
  617. go.hideFlags = HideFlags.DontSave;
  618. go.transform.SetParent(m_TextComponent.transform.parent);
  619. go.transform.SetAsFirstSibling();
  620. go.layer = gameObject.layer;
  621. caretRectTrans = go.GetComponent<RectTransform>();
  622. m_CachedInputRenderer = go.GetComponent<CanvasRenderer>();
  623. m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
  624. // Needed as if any layout is present we want the caret to always be the same as the text area.
  625. go.AddComponent<LayoutElement>().ignoreLayout = true;
  626. AssignPositioningIfNeeded();
  627. }
  628. }
  629. // If we have a cached renderer then we had OnDisable called so just restore the material.
  630. if (m_CachedInputRenderer != null)
  631. m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
  632. if (m_TextComponent != null)
  633. {
  634. m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty);
  635. m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel);
  636. //m_TextComponent.ignoreRectMaskCulling = multiLine;
  637. m_DefaultTransformPosition = m_TextComponent.rectTransform.localPosition;
  638. // Cache reference to Vertical Scrollbar RectTransform and add listener.
  639. if (m_VerticalScrollbar != null)
  640. {
  641. m_TextComponent.ignoreRectMaskCulling = true;
  642. m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange);
  643. }
  644. UpdateLabel();
  645. }
  646. // Subscribe to event fired when text object has been regenerated.
  647. TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
  648. }
  649. protected override void OnDisable()
  650. {
  651. // the coroutine will be terminated, so this will ensure it restarts when we are next activated
  652. m_BlinkCoroutine = null;
  653. DeactivateInputField();
  654. if (m_TextComponent != null)
  655. {
  656. m_TextComponent.UnregisterDirtyVerticesCallback(MarkGeometryAsDirty);
  657. m_TextComponent.UnregisterDirtyVerticesCallback(UpdateLabel);
  658. if (m_VerticalScrollbar != null)
  659. m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange);
  660. }
  661. CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
  662. // Clear needs to be called otherwise sync never happens as the object is disabled.
  663. if (m_CachedInputRenderer != null)
  664. m_CachedInputRenderer.Clear();
  665. if (m_Mesh != null)
  666. DestroyImmediate(m_Mesh);
  667. m_Mesh = null;
  668. // Unsubscribe to event triggered when text object has been regenerated
  669. TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
  670. base.OnDisable();
  671. }
  672. /// <summary>
  673. /// Method used to update the tracking of the caret position when the text object has been regenerated.
  674. /// </summary>
  675. /// <param name="obj"></param>
  676. private void ON_TEXT_CHANGED(UnityEngine.Object obj)
  677. {
  678. if (obj == m_TextComponent && Application.isPlaying)
  679. {
  680. caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
  681. caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  682. //Debug.Log("Updating Caret position - Caret Position: " + m_CaretPosition + " Caret Select Position: " + m_CaretSelectPosition);
  683. }
  684. }
  685. IEnumerator CaretBlink()
  686. {
  687. // Always ensure caret is initially visible since it can otherwise be confusing for a moment.
  688. m_CaretVisible = true;
  689. yield return null;
  690. while (/*isFocused &&*/ m_CaretBlinkRate > 0)
  691. {
  692. // the blink rate is expressed as a frequency
  693. float blinkPeriod = 1f / m_CaretBlinkRate;
  694. // the caret should be ON if we are in the first half of the blink period
  695. bool blinkState = (Time.unscaledTime - m_BlinkStartTime) % blinkPeriod < blinkPeriod / 2;
  696. if (m_CaretVisible != blinkState)
  697. {
  698. m_CaretVisible = blinkState;
  699. if (!hasSelection)
  700. MarkGeometryAsDirty();
  701. }
  702. // Then wait again.
  703. yield return null;
  704. }
  705. m_BlinkCoroutine = null;
  706. }
  707. void SetCaretVisible()
  708. {
  709. if (!m_AllowInput)
  710. return;
  711. m_CaretVisible = true;
  712. m_BlinkStartTime = Time.unscaledTime;
  713. SetCaretActive();
  714. }
  715. // SetCaretActive will not set the caret immediately visible - it will wait for the next time to blink.
  716. // However, it will handle things correctly if the blink speed changed from zero to non-zero or non-zero to zero.
  717. void SetCaretActive()
  718. {
  719. if (!m_AllowInput)
  720. return;
  721. if (m_CaretBlinkRate > 0.0f)
  722. {
  723. if (m_BlinkCoroutine == null)
  724. m_BlinkCoroutine = StartCoroutine(CaretBlink());
  725. }
  726. else
  727. {
  728. m_CaretVisible = true;
  729. }
  730. }
  731. protected void OnFocus()
  732. {
  733. if (m_OnFocusSelectAll)
  734. SelectAll();
  735. }
  736. protected void SelectAll()
  737. {
  738. m_isSelectAll = true;
  739. stringPositionInternal = text.Length;
  740. stringSelectPositionInternal = 0;
  741. }
  742. /// <summary>
  743. /// Move to the end of the text.
  744. /// </summary>
  745. /// <param name="shift"></param>
  746. public void MoveTextEnd(bool shift)
  747. {
  748. if (m_isRichTextEditingAllowed)
  749. {
  750. int position = text.Length;
  751. if (shift)
  752. {
  753. stringSelectPositionInternal = position;
  754. }
  755. else
  756. {
  757. stringPositionInternal = position;
  758. stringSelectPositionInternal = stringPositionInternal;
  759. }
  760. }
  761. else
  762. {
  763. int position = m_TextComponent.textInfo.characterCount - 1;
  764. if (shift)
  765. {
  766. caretSelectPositionInternal = position;
  767. stringSelectPositionInternal = GetStringIndexFromCaretPosition(position);
  768. }
  769. else
  770. {
  771. caretPositionInternal = caretSelectPositionInternal = position;
  772. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(position);
  773. }
  774. }
  775. UpdateLabel();
  776. }
  777. /// <summary>
  778. /// Move to the start of the text.
  779. /// </summary>
  780. /// <param name="shift"></param>
  781. public void MoveTextStart(bool shift)
  782. {
  783. if (m_isRichTextEditingAllowed)
  784. {
  785. int position = 0;
  786. if (shift)
  787. {
  788. stringSelectPositionInternal = position;
  789. }
  790. else
  791. {
  792. stringPositionInternal = position;
  793. stringSelectPositionInternal = stringPositionInternal;
  794. }
  795. }
  796. else
  797. {
  798. int position = 0;
  799. if (shift)
  800. {
  801. caretSelectPositionInternal = position;
  802. stringSelectPositionInternal = GetStringIndexFromCaretPosition(position);
  803. }
  804. else
  805. {
  806. caretPositionInternal = caretSelectPositionInternal = position;
  807. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(position);
  808. }
  809. }
  810. UpdateLabel();
  811. }
  812. /// <summary>
  813. /// Move to the end of the current line of text.
  814. /// </summary>
  815. /// <param name="shift"></param>
  816. public void MoveToEndOfLine(bool shift, bool ctrl)
  817. {
  818. // Get the line the caret is currently located on.
  819. int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
  820. // Get the last character of the given line.
  821. int position = ctrl == true ? m_TextComponent.textInfo.characterCount - 1 : m_TextComponent.textInfo.lineInfo[currentLine].lastCharacterIndex;
  822. position = GetStringIndexFromCaretPosition(position);
  823. if (shift)
  824. {
  825. stringSelectPositionInternal = position;
  826. }
  827. else
  828. {
  829. stringPositionInternal = position;
  830. stringSelectPositionInternal = stringPositionInternal;
  831. }
  832. UpdateLabel();
  833. }
  834. /// <summary>
  835. /// Move to the start of the current line of text.
  836. /// </summary>
  837. /// <param name="shift"></param>
  838. public void MoveToStartOfLine(bool shift, bool ctrl)
  839. {
  840. // Get the line the caret is currently located on.
  841. int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
  842. // Get the last character of the given line.
  843. int position = ctrl == true ? 0 : m_TextComponent.textInfo.lineInfo[currentLine].firstCharacterIndex;
  844. position = GetStringIndexFromCaretPosition(position);
  845. if (shift)
  846. {
  847. stringSelectPositionInternal = position;
  848. }
  849. else
  850. {
  851. stringPositionInternal = position;
  852. stringSelectPositionInternal = stringPositionInternal;
  853. }
  854. UpdateLabel();
  855. }
  856. static string clipboard
  857. {
  858. get
  859. {
  860. return GUIUtility.systemCopyBuffer;
  861. }
  862. set
  863. {
  864. GUIUtility.systemCopyBuffer = value;
  865. }
  866. }
  867. private bool InPlaceEditing()
  868. {
  869. return !TouchScreenKeyboard.isSupported;
  870. }
  871. /// <summary>
  872. /// Update the text based on input.
  873. /// </summary>
  874. // TODO: Make LateUpdate a coroutine instead. Allows us to control the update to only be when the field is active.
  875. protected virtual void LateUpdate()
  876. {
  877. // Only activate if we are not already activated.
  878. if (m_ShouldActivateNextUpdate)
  879. {
  880. if (!isFocused)
  881. {
  882. ActivateInputFieldInternal();
  883. m_ShouldActivateNextUpdate = false;
  884. return;
  885. }
  886. // Reset as we are already activated.
  887. m_ShouldActivateNextUpdate = false;
  888. }
  889. // Update Scrollbar if needed
  890. if (m_IsScrollbarUpdateRequired)
  891. {
  892. UpdateScrollbar();
  893. m_IsScrollbarUpdateRequired = false;
  894. }
  895. //if (!isFocused && !m_VerticalScrollbarEventHandler.isSelected)
  896. //{
  897. // m_ForceDeactivation = true;
  898. // DeactivateInputField();
  899. // return;
  900. //}
  901. //if (!isFocused)
  902. //{
  903. // GameObject currentSelection = EventSystem.current == null ? null : EventSystem.current.currentSelectedGameObject;
  904. // if (currentSelection != null)
  905. // Debug.Log("Current Selection is: " + EventSystem.current.currentSelectedGameObject);
  906. // else
  907. // Debug.Log("No GameObject is selected...");
  908. //}
  909. if (InPlaceEditing() || !isFocused)
  910. return;
  911. //Debug.Log(this + " has focus...");
  912. AssignPositioningIfNeeded();
  913. if (m_Keyboard == null || !m_Keyboard.active)
  914. {
  915. if (m_Keyboard != null)
  916. {
  917. if (!m_ReadOnly)
  918. text = m_Keyboard.text;
  919. if (m_Keyboard.status == TouchScreenKeyboard.Status.Canceled)
  920. m_WasCanceled = true;
  921. if (m_Keyboard.status == TouchScreenKeyboard.Status.Done)
  922. OnSubmit(null);
  923. }
  924. OnDeselect(null);
  925. return;
  926. }
  927. string val = m_Keyboard.text;
  928. if (m_Text != val)
  929. {
  930. if (m_ReadOnly)
  931. {
  932. m_Keyboard.text = m_Text;
  933. }
  934. else
  935. {
  936. m_Text = "";
  937. for (int i = 0; i < val.Length; ++i)
  938. {
  939. char c = val[i];
  940. if (c == '\r' || (int)c == 3)
  941. c = '\n';
  942. if (onValidateInput != null)
  943. c = onValidateInput(m_Text, m_Text.Length, c);
  944. else if (characterValidation != CharacterValidation.None)
  945. c = Validate(m_Text, m_Text.Length, c);
  946. if (lineType == LineType.MultiLineSubmit && c == '\n')
  947. {
  948. m_Keyboard.text = m_Text;
  949. OnSubmit(null);
  950. OnDeselect(null);
  951. return;
  952. }
  953. if (c != 0)
  954. m_Text += c;
  955. }
  956. if (characterLimit > 0 && m_Text.Length > characterLimit)
  957. m_Text = m_Text.Substring(0, characterLimit);
  958. stringPositionInternal = stringSelectPositionInternal = m_Text.Length;
  959. // Set keyboard text before updating label, as we might have changed it with validation
  960. // and update label will take the old value from keyboard if we don't change it here
  961. if (m_Text != val)
  962. m_Keyboard.text = m_Text;
  963. SendOnValueChangedAndUpdateLabel();
  964. }
  965. }
  966. if (m_Keyboard.status == TouchScreenKeyboard.Status.Done)
  967. {
  968. if (m_Keyboard.status == TouchScreenKeyboard.Status.Canceled)
  969. m_WasCanceled = true;
  970. OnDeselect(null);
  971. }
  972. }
  973. private bool MayDrag(PointerEventData eventData)
  974. {
  975. return IsActive() &&
  976. IsInteractable() &&
  977. eventData.button == PointerEventData.InputButton.Left &&
  978. m_TextComponent != null &&
  979. m_Keyboard == null;
  980. }
  981. public virtual void OnBeginDrag(PointerEventData eventData)
  982. {
  983. if (!MayDrag(eventData))
  984. return;
  985. m_UpdateDrag = true;
  986. }
  987. public virtual void OnDrag(PointerEventData eventData)
  988. {
  989. if (!MayDrag(eventData))
  990. return;
  991. CaretPosition insertionSide;
  992. int insertionIndex = TMP_TextUtilities.GetCursorIndexFromPosition(m_TextComponent, eventData.position, eventData.pressEventCamera, out insertionSide);
  993. if (insertionSide == CaretPosition.Left)
  994. stringSelectPositionInternal = GetStringIndexFromCaretPosition(insertionIndex);
  995. else if (insertionSide == CaretPosition.Right)
  996. stringSelectPositionInternal = GetStringIndexFromCaretPosition(insertionIndex) + 1;
  997. caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  998. MarkGeometryAsDirty();
  999. m_DragPositionOutOfBounds = !RectTransformUtility.RectangleContainsScreenPoint(textViewport, eventData.position, eventData.pressEventCamera);
  1000. if (m_DragPositionOutOfBounds && m_DragCoroutine == null)
  1001. m_DragCoroutine = StartCoroutine(MouseDragOutsideRect(eventData));
  1002. eventData.Use();
  1003. }
  1004. IEnumerator MouseDragOutsideRect(PointerEventData eventData)
  1005. {
  1006. while (m_UpdateDrag && m_DragPositionOutOfBounds)
  1007. {
  1008. Vector2 localMousePos;
  1009. RectTransformUtility.ScreenPointToLocalPointInRectangle(textViewport, eventData.position, eventData.pressEventCamera, out localMousePos);
  1010. Rect rect = textViewport.rect;
  1011. if (multiLine)
  1012. {
  1013. if (localMousePos.y > rect.yMax)
  1014. MoveUp(true, true);
  1015. else if (localMousePos.y < rect.yMin)
  1016. MoveDown(true, true);
  1017. }
  1018. else
  1019. {
  1020. if (localMousePos.x < rect.xMin)
  1021. MoveLeft(true, false);
  1022. else if (localMousePos.x > rect.xMax)
  1023. MoveRight(true, false);
  1024. }
  1025. UpdateLabel();
  1026. float delay = multiLine ? kVScrollSpeed : kHScrollSpeed;
  1027. yield return new WaitForSeconds(delay);
  1028. //yield return new WaitForSecondsRealtime(delay); // Unity 5.4
  1029. }
  1030. m_DragCoroutine = null;
  1031. }
  1032. public virtual void OnEndDrag(PointerEventData eventData)
  1033. {
  1034. if (!MayDrag(eventData))
  1035. return;
  1036. m_UpdateDrag = false;
  1037. }
  1038. public override void OnPointerDown(PointerEventData eventData)
  1039. {
  1040. if (!MayDrag(eventData))
  1041. return;
  1042. EventSystem.current.SetSelectedGameObject(gameObject, eventData);
  1043. bool hadFocusBefore = m_AllowInput;
  1044. base.OnPointerDown(eventData);
  1045. if (!InPlaceEditing())
  1046. {
  1047. if (m_Keyboard == null || !m_Keyboard.active)
  1048. {
  1049. OnSelect(eventData);
  1050. return;
  1051. }
  1052. }
  1053. bool shift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
  1054. // Check for Double Click
  1055. bool isDoubleClick = false;
  1056. float timeStamp = Time.unscaledTime;
  1057. if (m_ClickStartTime + m_DoubleClickDelay > timeStamp)
  1058. isDoubleClick = true;
  1059. m_ClickStartTime = timeStamp;
  1060. // Only set caret position if we didn't just get focus now.
  1061. // Otherwise it will overwrite the select all on focus.
  1062. if (hadFocusBefore || !m_OnFocusSelectAll)
  1063. {
  1064. CaretPosition insertionSide;
  1065. int insertionIndex = TMP_TextUtilities.GetCursorIndexFromPosition(m_TextComponent, eventData.position, eventData.pressEventCamera, out insertionSide);
  1066. if (shift)
  1067. {
  1068. if (insertionSide == CaretPosition.Left)
  1069. stringSelectPositionInternal = GetStringIndexFromCaretPosition(insertionIndex);
  1070. else if (insertionSide == CaretPosition.Right)
  1071. stringSelectPositionInternal = GetStringIndexFromCaretPosition(insertionIndex) + 1;
  1072. }
  1073. else
  1074. {
  1075. if (insertionSide == CaretPosition.Left)
  1076. stringPositionInternal = stringSelectPositionInternal = GetStringIndexFromCaretPosition(insertionIndex);
  1077. else if (insertionSide == CaretPosition.Right)
  1078. stringPositionInternal = stringSelectPositionInternal = GetStringIndexFromCaretPosition(insertionIndex) + 1;
  1079. }
  1080. if (isDoubleClick)
  1081. {
  1082. int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextComponent, eventData.position, eventData.pressEventCamera);
  1083. if (wordIndex != -1)
  1084. {
  1085. // Select current word
  1086. caretPositionInternal = m_TextComponent.textInfo.wordInfo[wordIndex].firstCharacterIndex;
  1087. caretSelectPositionInternal = m_TextComponent.textInfo.wordInfo[wordIndex].lastCharacterIndex + 1;
  1088. stringPositionInternal = GetStringIndexFromCaretPosition(caretPositionInternal);
  1089. stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1090. }
  1091. else
  1092. {
  1093. // Select current character
  1094. caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
  1095. stringSelectPositionInternal += 1;
  1096. caretSelectPositionInternal = caretPositionInternal + 1;
  1097. caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1098. }
  1099. }
  1100. else
  1101. {
  1102. caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
  1103. }
  1104. }
  1105. UpdateLabel();
  1106. eventData.Use();
  1107. }
  1108. protected enum EditState
  1109. {
  1110. Continue,
  1111. Finish
  1112. }
  1113. protected EditState KeyPressed(Event evt)
  1114. {
  1115. var currentEventModifiers = evt.modifiers;
  1116. RuntimePlatform rp = Application.platform;
  1117. bool isMac = (rp == RuntimePlatform.OSXEditor || rp == RuntimePlatform.OSXPlayer);
  1118. bool ctrl = isMac ? (currentEventModifiers & EventModifiers.Command) != 0 : (currentEventModifiers & EventModifiers.Control) != 0;
  1119. bool shift = (currentEventModifiers & EventModifiers.Shift) != 0;
  1120. bool alt = (currentEventModifiers & EventModifiers.Alt) != 0;
  1121. bool ctrlOnly = ctrl && !alt && !shift;
  1122. switch (evt.keyCode)
  1123. {
  1124. case KeyCode.Backspace:
  1125. {
  1126. Backspace();
  1127. return EditState.Continue;
  1128. }
  1129. case KeyCode.Delete:
  1130. {
  1131. ForwardSpace();
  1132. return EditState.Continue;
  1133. }
  1134. case KeyCode.Home:
  1135. {
  1136. MoveToStartOfLine(shift, ctrl);
  1137. return EditState.Continue;
  1138. }
  1139. case KeyCode.End:
  1140. {
  1141. MoveToEndOfLine(shift, ctrl);
  1142. return EditState.Continue;
  1143. }
  1144. // Select All
  1145. case KeyCode.A:
  1146. {
  1147. if (ctrlOnly)
  1148. {
  1149. SelectAll();
  1150. return EditState.Continue;
  1151. }
  1152. break;
  1153. }
  1154. // Copy
  1155. case KeyCode.C:
  1156. {
  1157. if (ctrlOnly)
  1158. {
  1159. if (inputType != InputType.Password)
  1160. clipboard = GetSelectedString();
  1161. else
  1162. clipboard = "";
  1163. return EditState.Continue;
  1164. }
  1165. break;
  1166. }
  1167. // Paste
  1168. case KeyCode.V:
  1169. {
  1170. if (ctrlOnly)
  1171. {
  1172. Append(clipboard);
  1173. return EditState.Continue;
  1174. }
  1175. break;
  1176. }
  1177. // Cut
  1178. case KeyCode.X:
  1179. {
  1180. if (ctrlOnly)
  1181. {
  1182. if (inputType != InputType.Password)
  1183. clipboard = GetSelectedString();
  1184. else
  1185. clipboard = "";
  1186. Delete();
  1187. SendOnValueChangedAndUpdateLabel();
  1188. return EditState.Continue;
  1189. }
  1190. break;
  1191. }
  1192. case KeyCode.LeftArrow:
  1193. {
  1194. MoveLeft(shift, ctrl);
  1195. return EditState.Continue;
  1196. }
  1197. case KeyCode.RightArrow:
  1198. {
  1199. MoveRight(shift, ctrl);
  1200. return EditState.Continue;
  1201. }
  1202. case KeyCode.UpArrow:
  1203. {
  1204. MoveUp(shift);
  1205. return EditState.Continue;
  1206. }
  1207. case KeyCode.DownArrow:
  1208. {
  1209. MoveDown(shift);
  1210. return EditState.Continue;
  1211. }
  1212. case KeyCode.PageUp:
  1213. {
  1214. MovePageUp(shift);
  1215. return EditState.Continue;
  1216. }
  1217. case KeyCode.PageDown:
  1218. {
  1219. MovePageDown(shift);
  1220. return EditState.Continue;
  1221. }
  1222. // Submit
  1223. case KeyCode.Return:
  1224. case KeyCode.KeypadEnter:
  1225. {
  1226. if (lineType != LineType.MultiLineNewline)
  1227. {
  1228. return EditState.Finish;
  1229. }
  1230. break;
  1231. }
  1232. case KeyCode.Escape:
  1233. {
  1234. m_WasCanceled = true;
  1235. return EditState.Finish;
  1236. }
  1237. }
  1238. char c = evt.character;
  1239. // Don't allow return chars or tabulator key to be entered into single line fields.
  1240. if (!multiLine && (c == '\t' || c == '\r' || c == 10))
  1241. return EditState.Continue;
  1242. // Convert carriage return and end-of-text characters to newline.
  1243. if (c == '\r' || (int)c == 3)
  1244. c = '\n';
  1245. if (IsValidChar(c))
  1246. {
  1247. Append(c);
  1248. }
  1249. if (c == 0)
  1250. {
  1251. if (Input.compositionString.Length > 0)
  1252. {
  1253. UpdateLabel();
  1254. }
  1255. }
  1256. return EditState.Continue;
  1257. }
  1258. private bool IsValidChar(char c)
  1259. {
  1260. // Delete key on mac
  1261. if ((int)c == 127)
  1262. return false;
  1263. // Accept newline and tab
  1264. if (c == '\t' || c == '\n')
  1265. return true;
  1266. return m_TextComponent.font.HasCharacter(c, true);
  1267. }
  1268. /// <summary>
  1269. /// Handle the specified event.
  1270. /// </summary>
  1271. private Event m_ProcessingEvent = new Event();
  1272. public void ProcessEvent(Event e)
  1273. {
  1274. KeyPressed(e);
  1275. }
  1276. /// <summary>
  1277. ///
  1278. /// </summary>
  1279. /// <param name="eventData"></param>
  1280. public virtual void OnUpdateSelected(BaseEventData eventData)
  1281. {
  1282. if (!isFocused)
  1283. return;
  1284. bool consumedEvent = false;
  1285. while (Event.PopEvent(m_ProcessingEvent))
  1286. {
  1287. if (m_ProcessingEvent.rawType == EventType.KeyDown)
  1288. {
  1289. consumedEvent = true;
  1290. var shouldContinue = KeyPressed(m_ProcessingEvent);
  1291. if (shouldContinue == EditState.Finish)
  1292. {
  1293. SendOnSubmit();
  1294. DeactivateInputField();
  1295. break;
  1296. }
  1297. }
  1298. switch (m_ProcessingEvent.type)
  1299. {
  1300. case EventType.ValidateCommand:
  1301. case EventType.ExecuteCommand:
  1302. switch (m_ProcessingEvent.commandName)
  1303. {
  1304. case "SelectAll":
  1305. SelectAll();
  1306. consumedEvent = true;
  1307. break;
  1308. }
  1309. break;
  1310. }
  1311. }
  1312. if (consumedEvent)
  1313. UpdateLabel();
  1314. eventData.Use();
  1315. }
  1316. /// <summary>
  1317. ///
  1318. /// </summary>
  1319. /// <param name="eventData"></param>
  1320. public virtual void OnScroll(PointerEventData eventData)
  1321. {
  1322. if (m_TextComponent.preferredHeight < m_TextViewport.rect.height) return;
  1323. float scrollDirection = -eventData.scrollDelta.y;
  1324. m_ScrollPosition = m_ScrollPosition + (1f / m_TextComponent.textInfo.lineCount) * scrollDirection * m_ScrollSensitivity;
  1325. m_ScrollPosition = Mathf.Clamp01(m_ScrollPosition);
  1326. AdjustTextPositionRelativeToViewport(m_ScrollPosition);
  1327. // Disable focus until user re-selected the input field.
  1328. m_AllowInput = false;
  1329. if (m_VerticalScrollbar)
  1330. {
  1331. m_IsUpdatingScrollbarValues = true;
  1332. m_VerticalScrollbar.value = m_ScrollPosition;
  1333. //m_VerticalScrollbar.numberOfSteps = (int)(m_TextComponent.textInfo.lineCount / scrollSensitivity);
  1334. }
  1335. //Debug.Log("Scroll Position:" + m_ScrollPosition);
  1336. }
  1337. private string GetSelectedString()
  1338. {
  1339. if (!hasSelection)
  1340. return "";
  1341. int startPos = stringPositionInternal;
  1342. int endPos = stringSelectPositionInternal;
  1343. // Ensure pos is always less then selPos to make the code simpler
  1344. if (startPos > endPos)
  1345. {
  1346. int temp = startPos;
  1347. startPos = endPos;
  1348. endPos = temp;
  1349. }
  1350. //for (int i = m_CaretPosition; i < m_CaretSelectPosition; i++)
  1351. //{
  1352. // Debug.Log("Character [" + m_TextComponent.textInfo.characterInfo[i].character + "] using Style [" + m_TextComponent.textInfo.characterInfo[i].style + "] has been selected.");
  1353. //}
  1354. return text.Substring(startPos, endPos - startPos);
  1355. }
  1356. private int FindtNextWordBegin()
  1357. {
  1358. if (stringSelectPositionInternal + 1 >= text.Length)
  1359. return text.Length;
  1360. int spaceLoc = text.IndexOfAny(kSeparators, stringSelectPositionInternal + 1);
  1361. if (spaceLoc == -1)
  1362. spaceLoc = text.Length;
  1363. else
  1364. spaceLoc++;
  1365. return spaceLoc;
  1366. }
  1367. private void MoveRight(bool shift, bool ctrl)
  1368. {
  1369. if (hasSelection && !shift)
  1370. {
  1371. // By convention, if we have a selection and move right without holding shift,
  1372. // we just place the cursor at the end.
  1373. stringPositionInternal = stringSelectPositionInternal = Mathf.Max(stringPositionInternal, stringSelectPositionInternal);
  1374. caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1375. #if TMP_DEBUG_MODE
  1376. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1377. #endif
  1378. return;
  1379. }
  1380. int position;
  1381. if (ctrl)
  1382. position = FindtNextWordBegin();
  1383. else
  1384. {
  1385. if (m_isRichTextEditingAllowed)
  1386. position = stringSelectPositionInternal + 1;
  1387. else
  1388. position = GetStringIndexFromCaretPosition(caretSelectPositionInternal + 1);
  1389. }
  1390. if (shift)
  1391. {
  1392. stringSelectPositionInternal = position;
  1393. caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1394. }
  1395. else
  1396. {
  1397. stringSelectPositionInternal = stringPositionInternal = position;
  1398. caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1399. }
  1400. #if TMP_DEBUG_MODE
  1401. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1402. #endif
  1403. }
  1404. private int FindtPrevWordBegin()
  1405. {
  1406. if (stringSelectPositionInternal - 2 < 0)
  1407. return 0;
  1408. int spaceLoc = text.LastIndexOfAny(kSeparators, stringSelectPositionInternal - 2);
  1409. if (spaceLoc == -1)
  1410. spaceLoc = 0;
  1411. else
  1412. spaceLoc++;
  1413. return spaceLoc;
  1414. }
  1415. private void MoveLeft(bool shift, bool ctrl)
  1416. {
  1417. if (hasSelection && !shift)
  1418. {
  1419. // By convention, if we have a selection and move left without holding shift,
  1420. // we just place the cursor at the start.
  1421. stringPositionInternal = stringSelectPositionInternal = Mathf.Min(stringPositionInternal, stringSelectPositionInternal);
  1422. caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1423. #if TMP_DEBUG_MODE
  1424. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1425. #endif
  1426. return;
  1427. }
  1428. int position;
  1429. if (ctrl)
  1430. position = FindtPrevWordBegin();
  1431. else
  1432. {
  1433. if (m_isRichTextEditingAllowed)
  1434. position = stringSelectPositionInternal - 1;
  1435. else
  1436. position = GetStringIndexFromCaretPosition(caretSelectPositionInternal - 1);
  1437. }
  1438. if (shift)
  1439. {
  1440. stringSelectPositionInternal = position;
  1441. caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1442. }
  1443. else
  1444. {
  1445. stringSelectPositionInternal = stringPositionInternal = position;
  1446. caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  1447. }
  1448. #if TMP_DEBUG_MODE
  1449. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1450. #endif
  1451. }
  1452. private int LineUpCharacterPosition(int originalPos, bool goToFirstChar)
  1453. {
  1454. if (originalPos >= m_TextComponent.textInfo.characterCount)
  1455. originalPos -= 1;
  1456. TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
  1457. int originLine = originChar.lineNumber;
  1458. // We are on the first line return first character
  1459. if (originLine - 1 < 0)
  1460. return goToFirstChar ? 0 : originalPos;
  1461. int endCharIdx = m_TextComponent.textInfo.lineInfo[originLine].firstCharacterIndex - 1;
  1462. int closest = -1;
  1463. float distance = TMP_Math.FLOAT_MAX;
  1464. float range = 0;
  1465. for (int i = m_TextComponent.textInfo.lineInfo[originLine - 1].firstCharacterIndex; i < endCharIdx; ++i)
  1466. {
  1467. TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
  1468. float d = originChar.origin - currentChar.origin;
  1469. float r = d / (currentChar.xAdvance - currentChar.origin);
  1470. if (r >= 0 && r <= 1)
  1471. {
  1472. if (r < 0.5f)
  1473. return i;
  1474. else
  1475. return i + 1;
  1476. }
  1477. d = Mathf.Abs(d);
  1478. if (d < distance)
  1479. {
  1480. closest = i;
  1481. distance = d;
  1482. range = r;
  1483. }
  1484. }
  1485. if (closest == -1) return endCharIdx;
  1486. //Debug.Log("Returning nearest character with Range = " + range);
  1487. if (range < 0.5f)
  1488. return closest;
  1489. else
  1490. return closest + 1;
  1491. }
  1492. private int LineDownCharacterPosition(int originalPos, bool goToLastChar)
  1493. {
  1494. if (originalPos >= m_TextComponent.textInfo.characterCount)
  1495. return m_TextComponent.textInfo.characterCount - 1; // text.Length;
  1496. TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
  1497. int originLine = originChar.lineNumber;
  1498. //// We are on the last line return last character
  1499. if (originLine + 1 >= m_TextComponent.textInfo.lineCount)
  1500. return goToLastChar ? m_TextComponent.textInfo.characterCount - 1 : originalPos;
  1501. // Need to determine end line for next line.
  1502. int endCharIdx = m_TextComponent.textInfo.lineInfo[originLine + 1].lastCharacterIndex;
  1503. int closest = -1;
  1504. float distance = TMP_Math.FLOAT_MAX;
  1505. float range = 0;
  1506. for (int i = m_TextComponent.textInfo.lineInfo[originLine + 1].firstCharacterIndex; i < endCharIdx; ++i)
  1507. {
  1508. TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
  1509. float d = originChar.origin - currentChar.origin;
  1510. float r = d / (currentChar.xAdvance - currentChar.origin);
  1511. if (r >= 0 && r <= 1)
  1512. {
  1513. if (r < 0.5f)
  1514. return i;
  1515. else
  1516. return i + 1;
  1517. }
  1518. d = Mathf.Abs(d);
  1519. if (d < distance)
  1520. {
  1521. closest = i;
  1522. distance = d;
  1523. range = r;
  1524. }
  1525. }
  1526. if (closest == -1) return endCharIdx;
  1527. //Debug.Log("Returning nearest character with Range = " + range);
  1528. if (range < 0.5f)
  1529. return closest;
  1530. else
  1531. return closest + 1;
  1532. }
  1533. private int PageUpCharacterPosition(int originalPos, bool goToFirstChar)
  1534. {
  1535. if (originalPos >= m_TextComponent.textInfo.characterCount)
  1536. originalPos -= 1;
  1537. TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
  1538. int originLine = originChar.lineNumber;
  1539. // We are on the first line return first character
  1540. if (originLine - 1 < 0)
  1541. return goToFirstChar ? 0 : originalPos;
  1542. float viewportHeight = m_TextViewport.rect.height;
  1543. int newLine = originLine - 1;
  1544. // Iterate through each subsequent line to find the first baseline that is not visible in the viewport.
  1545. for (; newLine > 0; newLine--)
  1546. {
  1547. if (m_TextComponent.textInfo.lineInfo[newLine].baseline > m_TextComponent.textInfo.lineInfo[originLine].baseline + viewportHeight)
  1548. break;
  1549. }
  1550. int endCharIdx = m_TextComponent.textInfo.lineInfo[newLine].lastCharacterIndex;
  1551. int closest = -1;
  1552. float distance = TMP_Math.FLOAT_MAX;
  1553. float range = 0;
  1554. for (int i = m_TextComponent.textInfo.lineInfo[newLine].firstCharacterIndex; i < endCharIdx; ++i)
  1555. {
  1556. TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
  1557. float d = originChar.origin - currentChar.origin;
  1558. float r = d / (currentChar.xAdvance - currentChar.origin);
  1559. if (r >= 0 && r <= 1)
  1560. {
  1561. if (r < 0.5f)
  1562. return i;
  1563. else
  1564. return i + 1;
  1565. }
  1566. d = Mathf.Abs(d);
  1567. if (d < distance)
  1568. {
  1569. closest = i;
  1570. distance = d;
  1571. range = r;
  1572. }
  1573. }
  1574. if (closest == -1) return endCharIdx;
  1575. //Debug.Log("Returning nearest character with Range = " + range);
  1576. if (range < 0.5f)
  1577. return closest;
  1578. else
  1579. return closest + 1;
  1580. }
  1581. private int PageDownCharacterPosition(int originalPos, bool goToLastChar)
  1582. {
  1583. if (originalPos >= m_TextComponent.textInfo.characterCount)
  1584. return m_TextComponent.textInfo.characterCount - 1;
  1585. TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
  1586. int originLine = originChar.lineNumber;
  1587. // We are on the last line return last character
  1588. if (originLine + 1 >= m_TextComponent.textInfo.lineCount)
  1589. return goToLastChar ? m_TextComponent.textInfo.characterCount - 1 : originalPos;
  1590. float viewportHeight = m_TextViewport.rect.height;
  1591. int newLine = originLine + 1;
  1592. // Iterate through each subsequent line to find the first baseline that is not visible in the viewport.
  1593. for (; newLine < m_TextComponent.textInfo.lineCount - 1; newLine++)
  1594. {
  1595. if (m_TextComponent.textInfo.lineInfo[newLine].baseline < m_TextComponent.textInfo.lineInfo[originLine].baseline - viewportHeight)
  1596. break;
  1597. }
  1598. // Need to determine end line for next line.
  1599. int endCharIdx = m_TextComponent.textInfo.lineInfo[newLine].lastCharacterIndex;
  1600. int closest = -1;
  1601. float distance = TMP_Math.FLOAT_MAX;
  1602. float range = 0;
  1603. for (int i = m_TextComponent.textInfo.lineInfo[newLine].firstCharacterIndex; i < endCharIdx; ++i)
  1604. {
  1605. TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
  1606. float d = originChar.origin - currentChar.origin;
  1607. float r = d / (currentChar.xAdvance - currentChar.origin);
  1608. if (r >= 0 && r <= 1)
  1609. {
  1610. if (r < 0.5f)
  1611. return i;
  1612. else
  1613. return i + 1;
  1614. }
  1615. d = Mathf.Abs(d);
  1616. if (d < distance)
  1617. {
  1618. closest = i;
  1619. distance = d;
  1620. range = r;
  1621. }
  1622. }
  1623. if (closest == -1) return endCharIdx;
  1624. if (range < 0.5f)
  1625. return closest;
  1626. else
  1627. return closest + 1;
  1628. }
  1629. private void MoveDown(bool shift)
  1630. {
  1631. MoveDown(shift, true);
  1632. }
  1633. private void MoveDown(bool shift, bool goToLastChar)
  1634. {
  1635. if (hasSelection && !shift)
  1636. {
  1637. // If we have a selection and press down without shift,
  1638. // set caret to end of selection before we move it down.
  1639. caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
  1640. }
  1641. int position = multiLine ? LineDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : m_TextComponent.textInfo.characterCount - 1; // text.Length;
  1642. if (shift)
  1643. {
  1644. caretSelectPositionInternal = position;
  1645. stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1646. }
  1647. else
  1648. {
  1649. caretSelectPositionInternal = caretPositionInternal = position;
  1650. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1651. }
  1652. #if TMP_DEBUG_MODE
  1653. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1654. #endif
  1655. }
  1656. private void MoveUp(bool shift)
  1657. {
  1658. MoveUp(shift, true);
  1659. }
  1660. private void MoveUp(bool shift, bool goToFirstChar)
  1661. {
  1662. if (hasSelection && !shift)
  1663. {
  1664. // If we have a selection and press up without shift,
  1665. // set caret position to start of selection before we move it up.
  1666. caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
  1667. }
  1668. int position = multiLine ? LineUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
  1669. if (shift)
  1670. {
  1671. caretSelectPositionInternal = position;
  1672. stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1673. }
  1674. else
  1675. {
  1676. caretSelectPositionInternal = caretPositionInternal = position;
  1677. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1678. }
  1679. #if TMP_DEBUG_MODE
  1680. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1681. #endif
  1682. }
  1683. private void MovePageUp(bool shift)
  1684. {
  1685. MovePageUp(shift, true);
  1686. }
  1687. private void MovePageUp(bool shift, bool goToFirstChar)
  1688. {
  1689. if (hasSelection && !shift)
  1690. {
  1691. // If we have a selection and press up without shift,
  1692. // set caret position to start of selection before we move it up.
  1693. caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
  1694. }
  1695. int position = multiLine ? PageUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
  1696. if (shift)
  1697. {
  1698. caretSelectPositionInternal = position;
  1699. stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1700. }
  1701. else
  1702. {
  1703. caretSelectPositionInternal = caretPositionInternal = position;
  1704. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1705. }
  1706. // Scroll to top of viewport
  1707. //int currentLine = m_TextComponent.textInfo.characterInfo[position].lineNumber;
  1708. //float lineAscender = m_TextComponent.textInfo.lineInfo[currentLine].ascender;
  1709. // Adjust text area up or down if not in single line mode.
  1710. if (m_LineType != LineType.SingleLine)
  1711. {
  1712. float offset = m_TextViewport.rect.height; // m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + lineAscender);
  1713. float topTextBounds = m_TextComponent.rectTransform.position.y + m_TextComponent.textBounds.max.y;
  1714. float topViewportBounds = m_TextViewport.position.y + m_TextViewport.rect.yMax;
  1715. offset = topViewportBounds > topTextBounds + offset ? offset : topViewportBounds - topTextBounds;
  1716. m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, offset);
  1717. AssignPositioningIfNeeded();
  1718. m_IsScrollbarUpdateRequired = true;
  1719. }
  1720. #if TMP_DEBUG_MODE
  1721. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1722. #endif
  1723. }
  1724. private void MovePageDown(bool shift)
  1725. {
  1726. MovePageDown(shift, true);
  1727. }
  1728. private void MovePageDown(bool shift, bool goToLastChar)
  1729. {
  1730. if (hasSelection && !shift)
  1731. {
  1732. // If we have a selection and press down without shift,
  1733. // set caret to end of selection before we move it down.
  1734. caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
  1735. }
  1736. int position = multiLine ? PageDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : m_TextComponent.textInfo.characterCount - 1;
  1737. if (shift)
  1738. {
  1739. caretSelectPositionInternal = position;
  1740. stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1741. }
  1742. else
  1743. {
  1744. caretSelectPositionInternal = caretPositionInternal = position;
  1745. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1746. }
  1747. // Scroll to top of viewport
  1748. //int currentLine = m_TextComponent.textInfo.characterInfo[position].lineNumber;
  1749. //float lineAscender = m_TextComponent.textInfo.lineInfo[currentLine].ascender;
  1750. // Adjust text area up or down if not in single line mode.
  1751. if (m_LineType != LineType.SingleLine)
  1752. {
  1753. float offset = m_TextViewport.rect.height; // m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + lineAscender);
  1754. float bottomTextBounds = m_TextComponent.rectTransform.position.y + m_TextComponent.textBounds.min.y;
  1755. float bottomViewportBounds = m_TextViewport.position.y + m_TextViewport.rect.yMin;
  1756. offset = bottomViewportBounds > bottomTextBounds + offset ? offset : bottomViewportBounds - bottomTextBounds;
  1757. m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, offset);
  1758. AssignPositioningIfNeeded();
  1759. m_IsScrollbarUpdateRequired = true;
  1760. }
  1761. #if TMP_DEBUG_MODE
  1762. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1763. #endif
  1764. }
  1765. private void Delete()
  1766. {
  1767. if (m_ReadOnly)
  1768. return;
  1769. if (stringPositionInternal == stringSelectPositionInternal)
  1770. return;
  1771. if (m_isRichTextEditingAllowed || m_isSelectAll)
  1772. {
  1773. // Handling of Delete when Rich Text is allowed.
  1774. if (stringPositionInternal < stringSelectPositionInternal)
  1775. {
  1776. m_Text = text.Substring(0, stringPositionInternal) + text.Substring(stringSelectPositionInternal, text.Length - stringSelectPositionInternal);
  1777. stringSelectPositionInternal = stringPositionInternal;
  1778. }
  1779. else
  1780. {
  1781. m_Text = text.Substring(0, stringSelectPositionInternal) + text.Substring(stringPositionInternal, text.Length - stringPositionInternal);
  1782. stringPositionInternal = stringSelectPositionInternal;
  1783. }
  1784. m_isSelectAll = false;
  1785. }
  1786. else
  1787. {
  1788. stringPositionInternal = GetStringIndexFromCaretPosition(caretPositionInternal);
  1789. stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
  1790. // Handling of Delete when Rich Text is not allowed.
  1791. if (caretPositionInternal < caretSelectPositionInternal)
  1792. {
  1793. m_Text = text.Substring(0, stringPositionInternal) + text.Substring(stringSelectPositionInternal, text.Length - stringSelectPositionInternal);
  1794. stringSelectPositionInternal = stringPositionInternal;
  1795. caretSelectPositionInternal = caretPositionInternal;
  1796. }
  1797. else
  1798. {
  1799. m_Text = text.Substring(0, stringSelectPositionInternal) + text.Substring(stringPositionInternal, text.Length - stringPositionInternal);
  1800. stringPositionInternal = stringSelectPositionInternal;
  1801. stringPositionInternal = stringSelectPositionInternal;
  1802. caretPositionInternal = caretSelectPositionInternal;
  1803. }
  1804. }
  1805. #if TMP_DEBUG_MODE
  1806. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1807. #endif
  1808. }
  1809. /// <summary>
  1810. /// Handling of DEL key
  1811. /// </summary>
  1812. private void ForwardSpace()
  1813. {
  1814. if (m_ReadOnly)
  1815. return;
  1816. if (hasSelection)
  1817. {
  1818. Delete();
  1819. SendOnValueChangedAndUpdateLabel();
  1820. }
  1821. else
  1822. {
  1823. if (m_isRichTextEditingAllowed)
  1824. {
  1825. if (stringPositionInternal < text.Length)
  1826. {
  1827. m_Text = text.Remove(stringPositionInternal, 1);
  1828. SendOnValueChangedAndUpdateLabel();
  1829. }
  1830. }
  1831. else
  1832. {
  1833. if (caretPositionInternal < m_TextComponent.textInfo.characterCount - 1)
  1834. {
  1835. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretPositionInternal);
  1836. m_Text = text.Remove(stringPositionInternal, 1);
  1837. SendOnValueChangedAndUpdateLabel();
  1838. }
  1839. }
  1840. }
  1841. #if TMP_DEBUG_MODE
  1842. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1843. #endif
  1844. }
  1845. /// <summary>
  1846. /// Handling of Backspace key
  1847. /// </summary>
  1848. private void Backspace()
  1849. {
  1850. if (m_ReadOnly)
  1851. return;
  1852. if (hasSelection)
  1853. {
  1854. Delete();
  1855. SendOnValueChangedAndUpdateLabel();
  1856. }
  1857. else
  1858. {
  1859. if (m_isRichTextEditingAllowed)
  1860. {
  1861. if (stringPositionInternal > 0)
  1862. {
  1863. m_Text = text.Remove(stringPositionInternal - 1, 1);
  1864. stringSelectPositionInternal = stringPositionInternal = stringPositionInternal - 1;
  1865. m_isLastKeyBackspace = true;
  1866. SendOnValueChangedAndUpdateLabel();
  1867. }
  1868. }
  1869. else
  1870. {
  1871. if (caretPositionInternal > 0)
  1872. {
  1873. m_Text = text.Remove(GetStringIndexFromCaretPosition(caretPositionInternal - 1), 1);
  1874. caretSelectPositionInternal = caretPositionInternal = caretPositionInternal - 1;
  1875. stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretPositionInternal);
  1876. }
  1877. m_isLastKeyBackspace = true;
  1878. SendOnValueChangedAndUpdateLabel();
  1879. }
  1880. }
  1881. #if TMP_DEBUG_MODE
  1882. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1883. #endif
  1884. }
  1885. /// <summary>
  1886. /// Append the specified text to the end of the current.
  1887. /// </summary>
  1888. protected virtual void Append(string input)
  1889. {
  1890. if (m_ReadOnly)
  1891. return;
  1892. if (!InPlaceEditing())
  1893. return;
  1894. for (int i = 0, imax = input.Length; i < imax; ++i)
  1895. {
  1896. char c = input[i];
  1897. if (c >= ' ' || c == '\t' || c == '\r' || c == 10 || c == '\n')
  1898. {
  1899. Append(c);
  1900. }
  1901. }
  1902. }
  1903. protected virtual void Append(char input)
  1904. {
  1905. if (m_ReadOnly)
  1906. return;
  1907. if (!InPlaceEditing())
  1908. return;
  1909. // If we have an input validator, validate the input first
  1910. if (onValidateInput != null)
  1911. input = onValidateInput(text, stringPositionInternal, input);
  1912. else if (characterValidation == CharacterValidation.CustomValidator)
  1913. {
  1914. input = Validate(text, stringPositionInternal, input);
  1915. if (input == 0) return;
  1916. SendOnValueChanged();
  1917. UpdateLabel();
  1918. return;
  1919. }
  1920. else if (characterValidation != CharacterValidation.None)
  1921. input = Validate(text, stringPositionInternal, input);
  1922. // If the input is invalid, skip it
  1923. if (input == 0)
  1924. return;
  1925. // Append the character and update the label
  1926. Insert(input);
  1927. }
  1928. // Insert the character and update the label.
  1929. private void Insert(char c)
  1930. {
  1931. if (m_ReadOnly)
  1932. return;
  1933. string replaceString = c.ToString();
  1934. Delete();
  1935. // Can't go past the character limit
  1936. if (characterLimit > 0 && text.Length >= characterLimit)
  1937. return;
  1938. m_Text = text.Insert(m_StringPosition, replaceString);
  1939. stringSelectPositionInternal = stringPositionInternal += replaceString.Length;
  1940. SendOnValueChanged();
  1941. #if TMP_DEBUG_MODE
  1942. Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
  1943. #endif
  1944. }
  1945. private void SendOnValueChangedAndUpdateLabel()
  1946. {
  1947. SendOnValueChanged();
  1948. UpdateLabel();
  1949. }
  1950. private void SendOnValueChanged()
  1951. {
  1952. if (onValueChanged != null)
  1953. onValueChanged.Invoke(text);
  1954. }
  1955. /// <summary>
  1956. /// Submit the input field's text.
  1957. /// </summary>
  1958. protected void SendOnEndEdit()
  1959. {
  1960. if (onEndEdit != null)
  1961. onEndEdit.Invoke(m_Text);
  1962. }
  1963. protected void SendOnSubmit()
  1964. {
  1965. if (onSubmit != null)
  1966. onSubmit.Invoke(m_Text);
  1967. }
  1968. protected void SendOnFocus()
  1969. {
  1970. if (onSelect != null)
  1971. onSelect.Invoke(m_Text);
  1972. }
  1973. protected void SendOnFocusLost()
  1974. {
  1975. if (onDeselect != null)
  1976. onDeselect.Invoke(m_Text);
  1977. }
  1978. protected void SendOnTextSelection()
  1979. {
  1980. m_isSelected = true;
  1981. if (onTextSelection != null)
  1982. onTextSelection.Invoke(m_Text, stringPositionInternal, stringSelectPositionInternal);
  1983. }
  1984. protected void SendOnEndTextSelection()
  1985. {
  1986. if (!m_isSelected) return;
  1987. if (onEndTextSelection != null)
  1988. onEndTextSelection.Invoke(m_Text, stringPositionInternal, stringSelectPositionInternal);
  1989. m_isSelected = false;
  1990. }
  1991. /// <summary>
  1992. /// Update the visual text Text.
  1993. /// </summary>
  1994. protected void UpdateLabel()
  1995. {
  1996. if (m_TextComponent != null && m_TextComponent.font != null)
  1997. {
  1998. // TextGenerator.Populate invokes a callback that's called for anything
  1999. // that needs to be updated when the data for that font has changed.
  2000. // This makes all Text components that use that font update their vertices.
  2001. // In turn, this makes the InputField that's associated with that Text component
  2002. // update its label by calling this UpdateLabel method.
  2003. // This is a recursive call we want to prevent, since it makes the InputField
  2004. // update based on font data that didn't yet finish executing, or alternatively
  2005. // hang on infinite recursion, depending on whether the cached value is cached
  2006. // before or after the calculation.
  2007. //
  2008. // This callback also occurs when assigning text to our Text component, i.e.,
  2009. // m_TextComponent.text = processed;
  2010. //m_PreventFontCallback = true;
  2011. string fullText;
  2012. if (Input.compositionString.Length > 0)
  2013. fullText = text.Substring(0, m_StringPosition) + Input.compositionString + text.Substring(m_StringPosition);
  2014. else
  2015. fullText = text;
  2016. string processed;
  2017. if (inputType == InputType.Password)
  2018. processed = new string(asteriskChar, fullText.Length);
  2019. else
  2020. processed = fullText;
  2021. bool isEmpty = string.IsNullOrEmpty(fullText);
  2022. if (m_Placeholder != null)
  2023. m_Placeholder.enabled = isEmpty; // && !isFocused;
  2024. // If not currently editing the text, set the visible range to the whole text.
  2025. // The UpdateLabel method will then truncate it to the part that fits inside the Text area.
  2026. // We can't do this when text is being edited since it would discard the current scroll,
  2027. // which is defined by means of the m_DrawStart and m_DrawEnd indices.
  2028. if (!isEmpty)
  2029. {
  2030. // // Determine what will actually fit into the given line
  2031. // Vector2 extents = m_TextComponent.rectTransform.rect.size;
  2032. // var settings = m_TextComponent.GetGenerationSettings(extents);
  2033. // settings.generateOutOfBounds = true;
  2034. // cachedInputTextGenerator.Populate(processed, settings);
  2035. // SetDrawRangeToContainCaretPosition(stringSelectPositionInternal - 1);
  2036. // processed = processed.Substring(m_DrawStart, Mathf.Min(m_DrawEnd, processed.Length) - m_DrawStart);
  2037. SetCaretVisible();
  2038. }
  2039. m_TextComponent.text = processed + "\u200B"; // Extra space is added for Caret tracking.
  2040. MarkGeometryAsDirty();
  2041. // Scrollbar should be updated.
  2042. m_IsScrollbarUpdateRequired = true;
  2043. //m_PreventFontCallback = false;
  2044. }
  2045. }
  2046. //private bool IsSelectionVisible()
  2047. //{
  2048. // if (m_DrawStart > stringPositionInternal || m_DrawStart > stringSelectPositionInternal)
  2049. // return false;
  2050. // if (m_DrawEnd < stringPositionInternal || m_DrawEnd < stringSelectPositionInternal)
  2051. // return false;
  2052. // return true;
  2053. //}
  2054. void UpdateScrollbar()
  2055. {
  2056. // Update Scrollbar
  2057. if (m_VerticalScrollbar)
  2058. {
  2059. float size = m_TextViewport.rect.height / m_TextComponent.preferredHeight;
  2060. m_IsUpdatingScrollbarValues = true;
  2061. m_VerticalScrollbar.size = size;
  2062. m_ScrollPosition = m_VerticalScrollbar.value = m_TextComponent.rectTransform.anchoredPosition.y / (m_TextComponent.preferredHeight - m_TextViewport.rect.height);
  2063. //m_VerticalScrollbar.numberOfSteps = (int)(m_TextComponent.textInfo.lineCount / 0.25f); // Replace by scroll sensitivity.
  2064. //Debug.Log("Updating Scrollbar... Value: " + m_VerticalScrollbar.value);
  2065. }
  2066. }
  2067. /// <summary>
  2068. /// Function to update the vertical position of the text container when OnValueChanged event is received from the Scrollbar.
  2069. /// </summary>
  2070. /// <param name="value"></param>
  2071. void OnScrollbarValueChange(float value)
  2072. {
  2073. if (m_IsUpdatingScrollbarValues) { m_IsUpdatingScrollbarValues = false; return; }
  2074. if (value < 0 || value > 1) return;
  2075. AdjustTextPositionRelativeToViewport(value);
  2076. m_ScrollPosition = value;
  2077. //Debug.Log("Scrollbar value is: " + value + " Transform POS: " + m_TextComponent.rectTransform.anchoredPosition);
  2078. }
  2079. /// <summary>
  2080. /// Adjusts the relative position of the body of the text relative to the viewport.
  2081. /// </summary>
  2082. /// <param name="relativePosition"></param>
  2083. void AdjustTextPositionRelativeToViewport (float relativePosition)
  2084. {
  2085. //Debug.Log("- Adjusting vertical text position to " + relativePosition);
  2086. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  2087. // Check to make sure we have valid data and lines to query.
  2088. if (textInfo == null || textInfo.lineInfo == null || textInfo.lineCount == 0 || textInfo.lineCount > textInfo.lineInfo.Length) return;
  2089. //m_TextComponent.rectTransform.anchoredPosition = new Vector2(m_TextComponent.rectTransform.anchoredPosition.x, (textHeight - viewportHeight) * relativePosition);
  2090. m_TextComponent.rectTransform.anchoredPosition = new Vector2(m_TextComponent.rectTransform.anchoredPosition.x, (m_TextComponent.preferredHeight - m_TextViewport.rect.height) * relativePosition);
  2091. AssignPositioningIfNeeded();
  2092. //Debug.Log("Text height: " + m_TextComponent.preferredHeight + " Viewport height: " + m_TextViewport.rect.height + " Adjusted RectTransform anchordedPosition:" + m_TextComponent.rectTransform.anchoredPosition + " Text Bounds: " + m_TextComponent.bounds.ToString("f3"));
  2093. }
  2094. private int GetCaretPositionFromStringIndex(int stringIndex)
  2095. {
  2096. int count = m_TextComponent.textInfo.characterCount;
  2097. for (int i = 0; i < count; i++)
  2098. {
  2099. if (m_TextComponent.textInfo.characterInfo[i].index >= stringIndex)
  2100. return i;
  2101. }
  2102. return count;
  2103. }
  2104. private int GetStringIndexFromCaretPosition(int caretPosition)
  2105. {
  2106. // Clamp values between 0 and character count.
  2107. ClampCaretPos(ref caretPosition);
  2108. return m_TextComponent.textInfo.characterInfo[caretPosition].index;
  2109. }
  2110. public void ForceLabelUpdate()
  2111. {
  2112. UpdateLabel();
  2113. }
  2114. private void MarkGeometryAsDirty()
  2115. {
  2116. #if UNITY_EDITOR
  2117. #if UNITY_2018_3_OR_NEWER
  2118. if (!Application.isPlaying || UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this))
  2119. return;
  2120. #else
  2121. if (!Application.isPlaying || UnityEditor.PrefabUtility.GetPrefabObject(gameObject) != null)
  2122. return;
  2123. #endif
  2124. #endif
  2125. CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
  2126. }
  2127. public virtual void Rebuild(CanvasUpdate update)
  2128. {
  2129. switch (update)
  2130. {
  2131. case CanvasUpdate.LatePreRender:
  2132. UpdateGeometry();
  2133. break;
  2134. }
  2135. }
  2136. public virtual void LayoutComplete()
  2137. { }
  2138. public virtual void GraphicUpdateComplete()
  2139. { }
  2140. private void UpdateGeometry()
  2141. {
  2142. #if UNITY_EDITOR
  2143. if (!Application.isPlaying)
  2144. return;
  2145. #endif
  2146. // No need to draw a cursor on mobile as its handled by the devices keyboard.
  2147. if (!shouldHideMobileInput)
  2148. return;
  2149. //if (m_CachedInputRenderer == null && m_TextComponent != null)
  2150. //{
  2151. // GameObject go = new GameObject(transform.name + " Input Caret");
  2152. // // Add MaskableGraphic Component
  2153. // go.AddComponent<TMP_SelectionCaret>();
  2154. // go.hideFlags = HideFlags.DontSave;
  2155. // go.transform.SetParent(m_TextComponent.transform.parent);
  2156. // go.transform.SetAsFirstSibling();
  2157. // go.layer = gameObject.layer;
  2158. // caretRectTrans = go.GetComponent<RectTransform>(); // go.AddComponent<RectTransform>();
  2159. // m_CachedInputRenderer = go.GetComponent<CanvasRenderer>(); // go.AddComponent<CanvasRenderer>();
  2160. // m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
  2161. // // Needed as if any layout is present we want the caret to always be the same as the text area.
  2162. // go.AddComponent<LayoutElement>().ignoreLayout = true;
  2163. // AssignPositioningIfNeeded();
  2164. //}
  2165. if (m_CachedInputRenderer == null)
  2166. return;
  2167. OnFillVBO(mesh);
  2168. m_CachedInputRenderer.SetMesh(mesh);
  2169. }
  2170. /// <summary>
  2171. /// Method to keep the Caret RectTransform properties in sync with the text object's RectTransform
  2172. /// </summary>
  2173. private void AssignPositioningIfNeeded()
  2174. {
  2175. if (m_TextComponent != null && caretRectTrans != null &&
  2176. (caretRectTrans.localPosition != m_TextComponent.rectTransform.localPosition ||
  2177. caretRectTrans.localRotation != m_TextComponent.rectTransform.localRotation ||
  2178. caretRectTrans.localScale != m_TextComponent.rectTransform.localScale ||
  2179. caretRectTrans.anchorMin != m_TextComponent.rectTransform.anchorMin ||
  2180. caretRectTrans.anchorMax != m_TextComponent.rectTransform.anchorMax ||
  2181. caretRectTrans.anchoredPosition != m_TextComponent.rectTransform.anchoredPosition ||
  2182. caretRectTrans.sizeDelta != m_TextComponent.rectTransform.sizeDelta ||
  2183. caretRectTrans.pivot != m_TextComponent.rectTransform.pivot))
  2184. {
  2185. caretRectTrans.localPosition = m_TextComponent.rectTransform.localPosition;
  2186. caretRectTrans.localRotation = m_TextComponent.rectTransform.localRotation;
  2187. caretRectTrans.localScale = m_TextComponent.rectTransform.localScale;
  2188. caretRectTrans.anchorMin = m_TextComponent.rectTransform.anchorMin;
  2189. caretRectTrans.anchorMax = m_TextComponent.rectTransform.anchorMax;
  2190. caretRectTrans.anchoredPosition = m_TextComponent.rectTransform.anchoredPosition;
  2191. caretRectTrans.sizeDelta = m_TextComponent.rectTransform.sizeDelta;
  2192. caretRectTrans.pivot = m_TextComponent.rectTransform.pivot;
  2193. // Get updated world corners of viewport.
  2194. //m_TextViewport.GetLocalCorners(m_ViewportCorners);
  2195. }
  2196. }
  2197. private void OnFillVBO(Mesh vbo)
  2198. {
  2199. using (var helper = new VertexHelper())
  2200. {
  2201. if (!isFocused && m_ResetOnDeActivation)
  2202. {
  2203. helper.FillMesh(vbo);
  2204. return;
  2205. }
  2206. if (isStringPositionDirty)
  2207. {
  2208. stringPositionInternal = GetStringIndexFromCaretPosition(m_CaretPosition);
  2209. stringSelectPositionInternal = GetStringIndexFromCaretPosition(m_CaretSelectPosition);
  2210. isStringPositionDirty = false;
  2211. }
  2212. if (!hasSelection)
  2213. {
  2214. GenerateCaret(helper, Vector2.zero);
  2215. SendOnEndTextSelection();
  2216. }
  2217. else
  2218. {
  2219. GenerateHightlight(helper, Vector2.zero);
  2220. SendOnTextSelection();
  2221. }
  2222. helper.FillMesh(vbo);
  2223. }
  2224. }
  2225. private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset)
  2226. {
  2227. if (!m_CaretVisible)
  2228. return;
  2229. if (m_CursorVerts == null)
  2230. {
  2231. CreateCursorVerts();
  2232. }
  2233. float width = m_CaretWidth;
  2234. // Optimize to only update the caret position when needed.
  2235. //
  2236. //
  2237. int characterCount = m_TextComponent.textInfo.characterCount;
  2238. Vector2 startPosition = Vector2.zero;
  2239. float height = 0;
  2240. TMP_CharacterInfo currentCharacter;
  2241. // Get the position of the Caret based on position in the string.
  2242. caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
  2243. if (caretPositionInternal == 0)
  2244. {
  2245. currentCharacter = m_TextComponent.textInfo.characterInfo[0];
  2246. startPosition = new Vector2(currentCharacter.origin, currentCharacter.descender);
  2247. height = currentCharacter.ascender - currentCharacter.descender;
  2248. }
  2249. else if (caretPositionInternal < characterCount)
  2250. {
  2251. currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal];
  2252. startPosition = new Vector2(currentCharacter.origin, currentCharacter.descender);
  2253. height = currentCharacter.ascender - currentCharacter.descender;
  2254. }
  2255. else
  2256. {
  2257. currentCharacter = m_TextComponent.textInfo.characterInfo[characterCount - 1];
  2258. startPosition = new Vector2(currentCharacter.xAdvance, currentCharacter.descender);
  2259. height = currentCharacter.ascender - currentCharacter.descender;
  2260. }
  2261. //Debug.Log("String Char [" + m_Text[m_StringPosition] + "] at Index:" + m_StringPosition + " Caret Char [" + currentCharacter.character + "] at Index:" + caretPositionInternal);
  2262. // Adjust the position of the RectTransform based on the caret position in the viewport (only if we have focus).
  2263. if (isFocused && startPosition != m_LastPosition || m_forceRectTransformAdjustment)
  2264. AdjustRectTransformRelativeToViewport(startPosition, height, currentCharacter.isVisible);
  2265. m_LastPosition = startPosition;
  2266. // Clamp Caret height
  2267. float top = startPosition.y + height;
  2268. float bottom = top - height; // Mathf.Min(height, m_TextComponent.rectTransform.rect.height);
  2269. m_CursorVerts[0].position = new Vector3(startPosition.x, bottom, 0.0f);
  2270. m_CursorVerts[1].position = new Vector3(startPosition.x, top, 0.0f);
  2271. m_CursorVerts[2].position = new Vector3(startPosition.x + width, top, 0.0f);
  2272. m_CursorVerts[3].position = new Vector3(startPosition.x + width, bottom, 0.0f);
  2273. // Set Vertex Color for the caret color.
  2274. m_CursorVerts[0].color = caretColor;
  2275. m_CursorVerts[1].color = caretColor;
  2276. m_CursorVerts[2].color = caretColor;
  2277. m_CursorVerts[3].color = caretColor;
  2278. vbo.AddUIVertexQuad(m_CursorVerts);
  2279. int screenHeight = Screen.height;
  2280. // Removed multiple display support until it supports none native resolutions(case 741751)
  2281. //int displayIndex = m_TextComponent.canvas.targetDisplay;
  2282. //if (Screen.fullScreen && displayIndex < Display.displays.Length)
  2283. // screenHeight = Display.displays[displayIndex].renderingHeight;
  2284. startPosition.y = screenHeight - startPosition.y;
  2285. Input.compositionCursorPos = startPosition;
  2286. //Debug.Log("Text Position: " + m_TextComponent.rectTransform.position + " Local Position: " + m_TextComponent.rectTransform.localPosition);
  2287. }
  2288. private void CreateCursorVerts()
  2289. {
  2290. m_CursorVerts = new UIVertex[4];
  2291. for (int i = 0; i < m_CursorVerts.Length; i++)
  2292. {
  2293. m_CursorVerts[i] = UIVertex.simpleVert;
  2294. m_CursorVerts[i].uv0 = Vector2.zero;
  2295. }
  2296. }
  2297. private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset)
  2298. {
  2299. TMP_TextInfo textInfo = m_TextComponent.textInfo;
  2300. caretPositionInternal = m_CaretPosition = GetCaretPositionFromStringIndex(stringPositionInternal);
  2301. caretSelectPositionInternal = m_CaretSelectPosition = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
  2302. //Debug.Log("StringPosition:" + caretPositionInternal + " StringSelectPosition:" + caretSelectPositionInternal);
  2303. // Adjust text RectTranform position to make sure it is visible in viewport.
  2304. Vector2 caretPosition;
  2305. float height = 0;
  2306. if (caretSelectPositionInternal < textInfo.characterCount)
  2307. {
  2308. caretPosition = new Vector2(textInfo.characterInfo[caretSelectPositionInternal].origin, textInfo.characterInfo[caretSelectPositionInternal].descender);
  2309. height = textInfo.characterInfo[caretSelectPositionInternal].ascender - textInfo.characterInfo[caretSelectPositionInternal].descender;
  2310. }
  2311. else
  2312. {
  2313. caretPosition = new Vector2(textInfo.characterInfo[caretSelectPositionInternal - 1].xAdvance, textInfo.characterInfo[caretSelectPositionInternal - 1].descender);
  2314. height = textInfo.characterInfo[caretSelectPositionInternal - 1].ascender - textInfo.characterInfo[caretSelectPositionInternal - 1].descender;
  2315. }
  2316. // TODO: Don't adjust the position of the RectTransform if Reset On Deactivation is disabled
  2317. // and we just selected the Input Field again.
  2318. AdjustRectTransformRelativeToViewport(caretPosition, height, true);
  2319. int startChar = Mathf.Max(0, caretPositionInternal);
  2320. int endChar = Mathf.Max(0, caretSelectPositionInternal);
  2321. // Ensure pos is always less then selPos to make the code simpler
  2322. if (startChar > endChar)
  2323. {
  2324. int temp = startChar;
  2325. startChar = endChar;
  2326. endChar = temp;
  2327. }
  2328. endChar -= 1;
  2329. //Debug.Log("Updating Highlight... Caret Position: " + startChar + " Caret Select POS: " + endChar);
  2330. int currentLineIndex = textInfo.characterInfo[startChar].lineNumber;
  2331. int nextLineStartIdx = textInfo.lineInfo[currentLineIndex].lastCharacterIndex;
  2332. UIVertex vert = UIVertex.simpleVert;
  2333. vert.uv0 = Vector2.zero;
  2334. vert.color = selectionColor;
  2335. int currentChar = startChar;
  2336. while (currentChar <= endChar && currentChar < textInfo.characterCount)
  2337. {
  2338. if (currentChar == nextLineStartIdx || currentChar == endChar)
  2339. {
  2340. TMP_CharacterInfo startCharInfo = textInfo.characterInfo[startChar];
  2341. TMP_CharacterInfo endCharInfo = textInfo.characterInfo[currentChar];
  2342. // Extra check to handle Carriage Return
  2343. if (currentChar > 0 && endCharInfo.character == 10 && textInfo.characterInfo[currentChar - 1].character == 13)
  2344. endCharInfo = textInfo.characterInfo[currentChar - 1];
  2345. Vector2 startPosition = new Vector2(startCharInfo.origin, textInfo.lineInfo[currentLineIndex].ascender);
  2346. Vector2 endPosition = new Vector2(endCharInfo.xAdvance, textInfo.lineInfo[currentLineIndex].descender);
  2347. var startIndex = vbo.currentVertCount;
  2348. vert.position = new Vector3(startPosition.x, endPosition.y, 0.0f);
  2349. vbo.AddVert(vert);
  2350. vert.position = new Vector3(endPosition.x, endPosition.y, 0.0f);
  2351. vbo.AddVert(vert);
  2352. vert.position = new Vector3(endPosition.x, startPosition.y, 0.0f);
  2353. vbo.AddVert(vert);
  2354. vert.position = new Vector3(startPosition.x, startPosition.y, 0.0f);
  2355. vbo.AddVert(vert);
  2356. vbo.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
  2357. vbo.AddTriangle(startIndex + 2, startIndex + 3, startIndex + 0);
  2358. startChar = currentChar + 1;
  2359. currentLineIndex++;
  2360. if (currentLineIndex < textInfo.lineCount)
  2361. nextLineStartIdx = textInfo.lineInfo[currentLineIndex].lastCharacterIndex;
  2362. }
  2363. currentChar++;
  2364. }
  2365. // Scrollbar should be updated.
  2366. m_IsScrollbarUpdateRequired = true;
  2367. }
  2368. /// <summary>
  2369. ///
  2370. /// </summary>
  2371. /// <param name="startPosition"></param>
  2372. /// <param name="height"></param>
  2373. /// <param name="isCharVisible"></param>
  2374. private void AdjustRectTransformRelativeToViewport(Vector2 startPosition, float height, bool isCharVisible)
  2375. {
  2376. //Debug.Log("Adjusting transform position relative to viewport.");
  2377. float viewportMin = m_TextViewport.rect.xMin;
  2378. float viewportMax = m_TextViewport.rect.xMax;
  2379. //Debug.Log("Viewport Rect: " + viewportMax + " Start Position: " + startPosition);
  2380. // Adjust the position of the RectTransform based on the caret position in the viewport.
  2381. float rightOffset = viewportMax - (m_TextComponent.rectTransform.anchoredPosition.x + startPosition.x + m_TextComponent.margin.z + m_CaretWidth);
  2382. if (rightOffset < 0f)
  2383. {
  2384. if (!multiLine || (multiLine && isCharVisible))
  2385. {
  2386. //Debug.Log("Shifting text to the right by " + rightOffset.ToString("f3"));
  2387. m_TextComponent.rectTransform.anchoredPosition += new Vector2(rightOffset, 0);
  2388. AssignPositioningIfNeeded();
  2389. }
  2390. }
  2391. float leftOffset = (m_TextComponent.rectTransform.anchoredPosition.x + startPosition.x - m_TextComponent.margin.x) - viewportMin;
  2392. if (leftOffset < 0f)
  2393. {
  2394. //Debug.Log("Shifting text to the left by " + leftOffset.ToString("f3"));
  2395. m_TextComponent.rectTransform.anchoredPosition += new Vector2(-leftOffset, 0);
  2396. AssignPositioningIfNeeded();
  2397. }
  2398. // Adjust text area up or down if not in single line mode.
  2399. if (m_LineType != LineType.SingleLine)
  2400. {
  2401. float topOffset = m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + startPosition.y + height);
  2402. if (topOffset < -0.0001f)
  2403. {
  2404. m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, topOffset);
  2405. AssignPositioningIfNeeded();
  2406. m_IsScrollbarUpdateRequired = true;
  2407. }
  2408. float bottomOffset = (m_TextComponent.rectTransform.anchoredPosition.y + startPosition.y) - m_TextViewport.rect.yMin;
  2409. if (bottomOffset < 0f)
  2410. {
  2411. m_TextComponent.rectTransform.anchoredPosition -= new Vector2(0, bottomOffset);
  2412. AssignPositioningIfNeeded();
  2413. m_IsScrollbarUpdateRequired = true;
  2414. }
  2415. }
  2416. // Special handling of backspace
  2417. if (m_isLastKeyBackspace)
  2418. {
  2419. float firstCharPosition = m_TextComponent.rectTransform.anchoredPosition.x + m_TextComponent.textInfo.characterInfo[0].origin - m_TextComponent.margin.x;
  2420. float lastCharPosition = m_TextComponent.rectTransform.anchoredPosition.x + m_TextComponent.textInfo.characterInfo[m_TextComponent.textInfo.characterCount - 1].origin + m_TextComponent.margin.z;
  2421. // Check if caret is at the left most position of the viewport
  2422. if (m_TextComponent.rectTransform.anchoredPosition.x + startPosition.x <= viewportMin + 0.0001f)
  2423. {
  2424. if (firstCharPosition < viewportMin)
  2425. {
  2426. float offset = Mathf.Min((viewportMax - viewportMin) / 2, viewportMin - firstCharPosition);
  2427. m_TextComponent.rectTransform.anchoredPosition += new Vector2(offset, 0);
  2428. AssignPositioningIfNeeded();
  2429. }
  2430. }
  2431. else if (lastCharPosition < viewportMax && firstCharPosition < viewportMin)
  2432. {
  2433. float offset = Mathf.Min(viewportMax - lastCharPosition, viewportMin - firstCharPosition);
  2434. m_TextComponent.rectTransform.anchoredPosition += new Vector2(offset, 0);
  2435. AssignPositioningIfNeeded();
  2436. }
  2437. m_isLastKeyBackspace = false;
  2438. }
  2439. m_forceRectTransformAdjustment = false;
  2440. }
  2441. /// <summary>
  2442. /// Validate the specified input.
  2443. /// </summary>
  2444. protected char Validate(string text, int pos, char ch)
  2445. {
  2446. // Validation is disabled
  2447. if (characterValidation == CharacterValidation.None || !enabled)
  2448. return ch;
  2449. if (characterValidation == CharacterValidation.Integer || characterValidation == CharacterValidation.Decimal)
  2450. {
  2451. // Integer and decimal
  2452. bool cursorBeforeDash = (pos == 0 && text.Length > 0 && text[0] == '-');
  2453. bool selectionAtStart = stringPositionInternal == 0 || stringSelectPositionInternal == 0;
  2454. if (!cursorBeforeDash)
  2455. {
  2456. if (ch >= '0' && ch <= '9') return ch;
  2457. if (ch == '-' && (pos == 0 || selectionAtStart)) return ch;
  2458. if (ch == '.' && characterValidation == CharacterValidation.Decimal && !text.Contains(".")) return ch;
  2459. }
  2460. }
  2461. else if (characterValidation == CharacterValidation.Digit)
  2462. {
  2463. if (ch >= '0' && ch <= '9') return ch;
  2464. }
  2465. else if (characterValidation == CharacterValidation.Alphanumeric)
  2466. {
  2467. // All alphanumeric characters
  2468. if (ch >= 'A' && ch <= 'Z') return ch;
  2469. if (ch >= 'a' && ch <= 'z') return ch;
  2470. if (ch >= '0' && ch <= '9') return ch;
  2471. }
  2472. else if (characterValidation == CharacterValidation.Name)
  2473. {
  2474. char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
  2475. char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
  2476. if (char.IsLetter(ch))
  2477. {
  2478. // Space followed by a letter -- make sure it's capitalized
  2479. if (char.IsLower(ch) && lastChar == ' ')
  2480. return char.ToUpper(ch);
  2481. // Uppercase letters are only allowed after spaces (and apostrophes)
  2482. if (char.IsUpper(ch) && lastChar != ' ' && lastChar != '\'')
  2483. return char.ToLower(ch);
  2484. // If character was already in correct case, return it as-is.
  2485. // Also, letters that are neither upper nor lower case are always allowed.
  2486. return ch;
  2487. }
  2488. else if (ch == '\'')
  2489. {
  2490. // Don't allow more than one apostrophe
  2491. if (lastChar != ' ' && lastChar != '\'' && nextChar != '\'' && !text.Contains("'"))
  2492. return ch;
  2493. }
  2494. else if (ch == ' ')
  2495. {
  2496. // Don't allow more than one space in a row
  2497. if (lastChar != ' ' && lastChar != '\'' && nextChar != ' ' && nextChar != '\'')
  2498. return ch;
  2499. }
  2500. }
  2501. else if (characterValidation == CharacterValidation.EmailAddress)
  2502. {
  2503. // From StackOverflow about allowed characters in email addresses:
  2504. // Uppercase and lowercase English letters (a-z, A-Z)
  2505. // Digits 0 to 9
  2506. // Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  2507. // Character . (dot, period, full stop) provided that it is not the first or last character,
  2508. // and provided also that it does not appear two or more times consecutively.
  2509. if (ch >= 'A' && ch <= 'Z') return ch;
  2510. if (ch >= 'a' && ch <= 'z') return ch;
  2511. if (ch >= '0' && ch <= '9') return ch;
  2512. if (ch == '@' && text.IndexOf('@') == -1) return ch;
  2513. if (kEmailSpecialCharacters.IndexOf(ch) != -1) return ch;
  2514. if (ch == '.')
  2515. {
  2516. char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
  2517. char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
  2518. if (lastChar != '.' && nextChar != '.')
  2519. return ch;
  2520. }
  2521. }
  2522. else if (characterValidation == CharacterValidation.Regex)
  2523. {
  2524. // Regex expression
  2525. if (Regex.IsMatch(ch.ToString(), m_RegexValue))
  2526. {
  2527. return ch;
  2528. }
  2529. }
  2530. else if (characterValidation == CharacterValidation.CustomValidator)
  2531. {
  2532. if (m_InputValidator != null)
  2533. {
  2534. char c = m_InputValidator.Validate(ref text, ref pos, ch);
  2535. m_Text = text;
  2536. stringSelectPositionInternal = stringPositionInternal = pos;
  2537. return c;
  2538. }
  2539. }
  2540. return (char)0;
  2541. }
  2542. public void ActivateInputField()
  2543. {
  2544. if (m_TextComponent == null || m_TextComponent.font == null || !IsActive() || !IsInteractable())
  2545. return;
  2546. if (isFocused)
  2547. {
  2548. if (m_Keyboard != null && !m_Keyboard.active)
  2549. {
  2550. m_Keyboard.active = true;
  2551. m_Keyboard.text = m_Text;
  2552. }
  2553. }
  2554. m_ShouldActivateNextUpdate = true;
  2555. }
  2556. private void ActivateInputFieldInternal()
  2557. {
  2558. if (EventSystem.current == null)
  2559. return;
  2560. if (EventSystem.current.currentSelectedGameObject != gameObject)
  2561. EventSystem.current.SetSelectedGameObject(gameObject);
  2562. if (TouchScreenKeyboard.isSupported)
  2563. {
  2564. if (Input.touchSupported)
  2565. {
  2566. TouchScreenKeyboard.hideInput = shouldHideMobileInput;
  2567. }
  2568. m_Keyboard = (inputType == InputType.Password) ?
  2569. TouchScreenKeyboard.Open(m_Text, keyboardType, false, multiLine, true) :
  2570. TouchScreenKeyboard.Open(m_Text, keyboardType, inputType == InputType.AutoCorrect, multiLine);
  2571. // Mimics OnFocus but as mobile doesn't properly support select all
  2572. // just set it to the end of the text (where it would move when typing starts)
  2573. MoveTextEnd(false);
  2574. }
  2575. else
  2576. {
  2577. Input.imeCompositionMode = IMECompositionMode.On;
  2578. OnFocus();
  2579. }
  2580. //m_StringPosition = m_StringSelectPosition = 0;
  2581. //m_CaretPosition = m_CaretSelectPosition = 0;
  2582. m_AllowInput = true;
  2583. m_OriginalText = text;
  2584. m_WasCanceled = false;
  2585. SetCaretVisible();
  2586. UpdateLabel();
  2587. }
  2588. public override void OnSelect(BaseEventData eventData)
  2589. {
  2590. //Debug.Log("OnSelect()");
  2591. base.OnSelect(eventData);
  2592. SendOnFocus();
  2593. ActivateInputField();
  2594. }
  2595. public virtual void OnPointerClick(PointerEventData eventData)
  2596. {
  2597. if (eventData.button != PointerEventData.InputButton.Left)
  2598. return;
  2599. ActivateInputField();
  2600. }
  2601. public void OnControlClick()
  2602. {
  2603. //Debug.Log("Input Field control click...");
  2604. }
  2605. public void DeactivateInputField()
  2606. {
  2607. //Debug.Log("Deactivate Input Field...");
  2608. // Not activated do nothing.
  2609. if (!m_AllowInput)
  2610. return;
  2611. m_HasDoneFocusTransition = false;
  2612. m_AllowInput = false;
  2613. if (m_Placeholder != null)
  2614. m_Placeholder.enabled = string.IsNullOrEmpty(m_Text);
  2615. if (m_TextComponent != null && IsInteractable())
  2616. {
  2617. if (m_WasCanceled && m_RestoreOriginalTextOnEscape)
  2618. text = m_OriginalText;
  2619. if (m_Keyboard != null)
  2620. {
  2621. m_Keyboard.active = false;
  2622. m_Keyboard = null;
  2623. }
  2624. if (m_ResetOnDeActivation)
  2625. {
  2626. m_StringPosition = m_StringSelectPosition = 0;
  2627. m_CaretPosition = m_CaretSelectPosition = 0;
  2628. m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
  2629. if (caretRectTrans != null)
  2630. caretRectTrans.localPosition = Vector3.zero;
  2631. //m_ForceDeactivation = false;
  2632. }
  2633. SendOnEndEdit();
  2634. SendOnEndTextSelection();
  2635. Input.imeCompositionMode = IMECompositionMode.Auto;
  2636. }
  2637. MarkGeometryAsDirty();
  2638. // Scrollbar should be updated.
  2639. m_IsScrollbarUpdateRequired = true;
  2640. }
  2641. public override void OnDeselect(BaseEventData eventData)
  2642. {
  2643. //return;
  2644. DeactivateInputField();
  2645. base.OnDeselect(eventData);
  2646. SendOnFocusLost();
  2647. }
  2648. public virtual void OnSubmit(BaseEventData eventData)
  2649. {
  2650. //Debug.Log("OnSubmit()");
  2651. if (!IsActive() || !IsInteractable())
  2652. return;
  2653. if (!isFocused)
  2654. m_ShouldActivateNextUpdate = true;
  2655. SendOnSubmit();
  2656. }
  2657. //public virtual void OnLostFocus(BaseEventData eventData)
  2658. //{
  2659. // if (!IsActive() || !IsInteractable())
  2660. // return;
  2661. //}
  2662. private void EnforceContentType()
  2663. {
  2664. switch (contentType)
  2665. {
  2666. case ContentType.Standard:
  2667. {
  2668. // Don't enforce line type for this content type.
  2669. m_InputType = InputType.Standard;
  2670. m_KeyboardType = TouchScreenKeyboardType.Default;
  2671. m_CharacterValidation = CharacterValidation.None;
  2672. return;
  2673. }
  2674. case ContentType.Autocorrected:
  2675. {
  2676. // Don't enforce line type for this content type.
  2677. m_InputType = InputType.AutoCorrect;
  2678. m_KeyboardType = TouchScreenKeyboardType.Default;
  2679. m_CharacterValidation = CharacterValidation.None;
  2680. return;
  2681. }
  2682. case ContentType.IntegerNumber:
  2683. {
  2684. m_LineType = LineType.SingleLine;
  2685. m_TextComponent.enableWordWrapping = false;
  2686. m_InputType = InputType.Standard;
  2687. m_KeyboardType = TouchScreenKeyboardType.NumberPad;
  2688. m_CharacterValidation = CharacterValidation.Integer;
  2689. return;
  2690. }
  2691. case ContentType.DecimalNumber:
  2692. {
  2693. m_LineType = LineType.SingleLine;
  2694. m_TextComponent.enableWordWrapping = false;
  2695. m_InputType = InputType.Standard;
  2696. m_KeyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
  2697. m_CharacterValidation = CharacterValidation.Decimal;
  2698. return;
  2699. }
  2700. case ContentType.Alphanumeric:
  2701. {
  2702. m_LineType = LineType.SingleLine;
  2703. m_TextComponent.enableWordWrapping = false;
  2704. m_InputType = InputType.Standard;
  2705. m_KeyboardType = TouchScreenKeyboardType.ASCIICapable;
  2706. m_CharacterValidation = CharacterValidation.Alphanumeric;
  2707. return;
  2708. }
  2709. case ContentType.Name:
  2710. {
  2711. m_LineType = LineType.SingleLine;
  2712. m_TextComponent.enableWordWrapping = false;
  2713. m_InputType = InputType.Standard;
  2714. m_KeyboardType = TouchScreenKeyboardType.Default;
  2715. m_CharacterValidation = CharacterValidation.Name;
  2716. return;
  2717. }
  2718. case ContentType.EmailAddress:
  2719. {
  2720. m_LineType = LineType.SingleLine;
  2721. m_TextComponent.enableWordWrapping = false;
  2722. m_InputType = InputType.Standard;
  2723. m_KeyboardType = TouchScreenKeyboardType.EmailAddress;
  2724. m_CharacterValidation = CharacterValidation.EmailAddress;
  2725. return;
  2726. }
  2727. case ContentType.Password:
  2728. {
  2729. m_LineType = LineType.SingleLine;
  2730. m_TextComponent.enableWordWrapping = false;
  2731. m_InputType = InputType.Password;
  2732. m_KeyboardType = TouchScreenKeyboardType.Default;
  2733. m_CharacterValidation = CharacterValidation.None;
  2734. return;
  2735. }
  2736. case ContentType.Pin:
  2737. {
  2738. m_LineType = LineType.SingleLine;
  2739. m_TextComponent.enableWordWrapping = false;
  2740. m_InputType = InputType.Password;
  2741. m_KeyboardType = TouchScreenKeyboardType.NumberPad;
  2742. m_CharacterValidation = CharacterValidation.Digit;
  2743. return;
  2744. }
  2745. default:
  2746. {
  2747. // Includes Custom type. Nothing should be enforced.
  2748. return;
  2749. }
  2750. }
  2751. }
  2752. void SetTextComponentWrapMode()
  2753. {
  2754. if (m_TextComponent == null)
  2755. return;
  2756. if (m_LineType == LineType.SingleLine)
  2757. m_TextComponent.enableWordWrapping = false;
  2758. else
  2759. m_TextComponent.enableWordWrapping = true;
  2760. }
  2761. // Control Rich Text option on the text component.
  2762. void SetTextComponentRichTextMode()
  2763. {
  2764. if (m_TextComponent == null)
  2765. return;
  2766. m_TextComponent.richText = m_RichText;
  2767. }
  2768. void SetToCustomIfContentTypeIsNot(params ContentType[] allowedContentTypes)
  2769. {
  2770. if (contentType == ContentType.Custom)
  2771. return;
  2772. for (int i = 0; i < allowedContentTypes.Length; i++)
  2773. if (contentType == allowedContentTypes[i])
  2774. return;
  2775. contentType = ContentType.Custom;
  2776. }
  2777. void SetToCustom()
  2778. {
  2779. if (contentType == ContentType.Custom)
  2780. return;
  2781. contentType = ContentType.Custom;
  2782. }
  2783. void SetToCustom(CharacterValidation characterValidation)
  2784. {
  2785. if (contentType == ContentType.Custom)
  2786. {
  2787. characterValidation = CharacterValidation.CustomValidator;
  2788. return;
  2789. }
  2790. contentType = ContentType.Custom;
  2791. characterValidation = CharacterValidation.CustomValidator;
  2792. }
  2793. protected override void DoStateTransition(SelectionState state, bool instant)
  2794. {
  2795. if (m_HasDoneFocusTransition)
  2796. state = SelectionState.Highlighted;
  2797. else if (state == SelectionState.Pressed)
  2798. m_HasDoneFocusTransition = true;
  2799. base.DoStateTransition(state, instant);
  2800. }
  2801. /// <summary>
  2802. /// Function to conveniently set the point size of both Placeholder and Input Field text object.
  2803. /// </summary>
  2804. /// <param name="pointSize"></param>
  2805. public void SetGlobalPointSize(float pointSize)
  2806. {
  2807. TMP_Text placeholderTextComponent = m_Placeholder as TMP_Text;
  2808. if (placeholderTextComponent != null) placeholderTextComponent.fontSize = pointSize;
  2809. textComponent.fontSize = pointSize;
  2810. }
  2811. /// <summary>
  2812. /// Function to conveniently set the Font Asset of both Placeholder and Input Field text object.
  2813. /// </summary>
  2814. /// <param name="fontAsset"></param>
  2815. public void SetGlobalFontAsset(TMP_FontAsset fontAsset)
  2816. {
  2817. TMP_Text placeholderTextComponent = m_Placeholder as TMP_Text;
  2818. if (placeholderTextComponent != null) placeholderTextComponent.font = fontAsset;
  2819. textComponent.font = fontAsset;
  2820. }
  2821. }
  2822. static class SetPropertyUtility
  2823. {
  2824. public static bool SetColor(ref Color currentValue, Color newValue)
  2825. {
  2826. if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a)
  2827. return false;
  2828. currentValue = newValue;
  2829. return true;
  2830. }
  2831. public static bool SetEquatableStruct<T>(ref T currentValue, T newValue) where T : IEquatable<T>
  2832. {
  2833. if (currentValue.Equals(newValue))
  2834. return false;
  2835. currentValue = newValue;
  2836. return true;
  2837. }
  2838. public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
  2839. {
  2840. if (currentValue.Equals(newValue))
  2841. return false;
  2842. currentValue = newValue;
  2843. return true;
  2844. }
  2845. public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
  2846. {
  2847. if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
  2848. return false;
  2849. currentValue = newValue;
  2850. return true;
  2851. }
  2852. }
  2853. }