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