//#define TMP_DEBUG_MODE
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
namespace TMPro
{
    /// 
    /// Editable text input field.
    /// 
    [AddComponentMenu("UI/TextMeshPro - Input Field", 11)]
        #if UNITY_2023_2_OR_NEWER
    [HelpURL("https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/TextMeshPro/index.html")]
    #else
    [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@3.2")]
    #endif
    public class TMP_InputField : Selectable,
        IUpdateSelectedHandler,
        IBeginDragHandler,
        IDragHandler,
        IEndDragHandler,
        IPointerClickHandler,
        ISubmitHandler,
        ICancelHandler,
        ICanvasElement,
        ILayoutElement,
        IScrollHandler
    {
        // Setting the content type acts as a shortcut for setting a combination of InputType, CharacterValidation, LineType, and TouchScreenKeyboardType
        public enum ContentType
        {
            Standard,
            Autocorrected,
            IntegerNumber,
            DecimalNumber,
            Alphanumeric,
            Name,
            EmailAddress,
            Password,
            Pin,
            Custom
        }
        public enum InputType
        {
            Standard,
            AutoCorrect,
            Password,
        }
        public enum CharacterValidation
        {
            None,
            Digit,
            Integer,
            Decimal,
            Alphanumeric,
            Name,
            Regex,
            EmailAddress,
            CustomValidator
        }
        public enum LineType
        {
            SingleLine,
            MultiLineSubmit,
            MultiLineNewline
        }
        public delegate char OnValidateInput(string text, int charIndex, char addedChar);
        [Serializable]
        public class SubmitEvent : UnityEvent { }
        [Serializable]
        public class OnChangeEvent : UnityEvent { }
        [Serializable]
        public class SelectionEvent : UnityEvent { }
        [Serializable]
        public class TextSelectionEvent : UnityEvent { }
        [Serializable]
        public class TouchScreenKeyboardEvent : UnityEvent { }
        protected TouchScreenKeyboard m_SoftKeyboard;
        static private readonly char[] kSeparators = { ' ', '.', ',', '\t', '\r', '\n' };
    #if UNITY_ANDROID
        static private bool s_IsQuestDeviceEvaluated = false;
    #endif // if UNITY_ANDROID
        static private bool s_IsQuestDevice = false;
        #region Exposed properties
        /// 
        /// Text Text used to display the input's value.
        /// 
        protected RectTransform m_RectTransform;
        [SerializeField]
        protected RectTransform m_TextViewport;
        protected RectMask2D m_TextComponentRectMask;
        protected RectMask2D m_TextViewportRectMask;
        //private Rect m_CachedViewportRect;
        [SerializeField]
        protected TMP_Text m_TextComponent;
        protected RectTransform m_TextComponentRectTransform;
        [SerializeField]
        protected Graphic m_Placeholder;
        [SerializeField]
        protected Scrollbar m_VerticalScrollbar;
        [SerializeField]
        protected TMP_ScrollbarEventHandler m_VerticalScrollbarEventHandler;
        //private bool m_ForceDeactivation;
        private bool m_IsDrivenByLayoutComponents = false;
        [SerializeField]
        private LayoutGroup m_LayoutGroup;
        private IScrollHandler m_IScrollHandlerParent;
        /// 
        /// Used to keep track of scroll position
        /// 
        private float m_ScrollPosition;
        /// 
        ///
        /// 
        [SerializeField]
        protected float m_ScrollSensitivity = 1.0f;
        //[SerializeField]
        //protected TMP_Text m_PlaceholderTextComponent;
        [SerializeField]
        private ContentType m_ContentType = ContentType.Standard;
        /// 
        /// Type of data expected by the input field.
        /// 
        [SerializeField]
        private InputType m_InputType = InputType.Standard;
        /// 
        /// The character used to hide text in password field.
        /// 
        [SerializeField]
        private char m_AsteriskChar = '*';
        /// 
        /// Keyboard type applies to mobile keyboards that get shown.
        /// 
        [SerializeField]
        private TouchScreenKeyboardType m_KeyboardType = TouchScreenKeyboardType.Default;
        [SerializeField]
        private LineType m_LineType = LineType.SingleLine;
        /// 
        /// Should hide mobile input field part of the virtual keyboard.
        /// 
        [SerializeField]
        private bool m_HideMobileInput = false;
        /// 
        /// Should hide soft / virtual keyboard.
        /// 
        [SerializeField]
        private bool m_HideSoftKeyboard = false;
        /// 
        /// What kind of validation to use with the input field's data.
        /// 
        [SerializeField]
        private CharacterValidation m_CharacterValidation = CharacterValidation.None;
        /// 
        /// The Regex expression used for validating the text input.
        /// 
        [SerializeField]
        private string m_RegexValue = string.Empty;
        /// 
        /// The point sized used by the placeholder and input text object.
        /// 
        [SerializeField]
        private float m_GlobalPointSize = 14;
        /// 
        /// Maximum number of characters allowed before input no longer works.
        /// 
        [SerializeField]
        private int m_CharacterLimit = 0;
        /// 
        /// Event delegates triggered when the input field submits its data.
        /// 
        [SerializeField]
        private SubmitEvent m_OnEndEdit = new SubmitEvent();
        /// 
        /// Event delegates triggered when the input field submits its data.
        /// 
        [SerializeField]
        private SubmitEvent m_OnSubmit = new SubmitEvent();
        /// 
        /// Event delegates triggered when the input field is focused.
        /// 
        [SerializeField]
        private SelectionEvent m_OnSelect = new SelectionEvent();
        /// 
        /// Event delegates triggered when the input field focus is lost.
        /// 
        [SerializeField]
        private SelectionEvent m_OnDeselect = new SelectionEvent();
        /// 
        /// Event delegates triggered when the text is selected / highlighted.
        /// 
        [SerializeField]
        private TextSelectionEvent m_OnTextSelection = new TextSelectionEvent();
        /// 
        /// Event delegates triggered when text is no longer select / highlighted.
        /// 
        [SerializeField]
        private TextSelectionEvent m_OnEndTextSelection = new TextSelectionEvent();
        /// 
        /// Event delegates triggered when the input field changes its data.
        /// 
        [SerializeField]
        private OnChangeEvent m_OnValueChanged = new OnChangeEvent();
        /// 
        /// Event delegates triggered when the status of the TouchScreenKeyboard changes.
        /// 
        [SerializeField]
        private TouchScreenKeyboardEvent m_OnTouchScreenKeyboardStatusChanged = new TouchScreenKeyboardEvent();
        /// 
        /// Custom validation callback.
        /// 
        [SerializeField]
        private OnValidateInput m_OnValidateInput;
        [SerializeField]
        private Color m_CaretColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
        [SerializeField]
        private bool m_CustomCaretColor = false;
        [SerializeField]
        private Color m_SelectionColor = new Color(168f / 255f, 206f / 255f, 255f / 255f, 192f / 255f);
        /// 
        /// Input field's value.
        /// 
        [SerializeField]
        [TextArea(5, 10)]
        protected string m_Text = string.Empty;
        [SerializeField]
        [Range(0f, 4f)]
        private float m_CaretBlinkRate = 0.85f;
        [SerializeField]
        [Range(1, 5)]
        private int m_CaretWidth = 1;
        [SerializeField]
        private bool m_ReadOnly = false;
        [SerializeField]
        private bool m_RichText = true;
        #endregion
        protected int m_StringPosition = 0;
        protected int m_StringSelectPosition = 0;
        protected int m_CaretPosition = 0;
        protected int m_CaretSelectPosition = 0;
        private RectTransform caretRectTrans = null;
        protected UIVertex[] m_CursorVerts = null;
        private CanvasRenderer m_CachedInputRenderer;
        private Vector2 m_LastPosition;
        [NonSerialized]
        protected Mesh m_Mesh;
        private bool m_AllowInput = false;
        //bool m_HasLostFocus = false;
        private bool m_ShouldActivateNextUpdate = false;
        private bool m_UpdateDrag = false;
        private bool m_DragPositionOutOfBounds = false;
        private const float kHScrollSpeed = 0.05f;
        private const float kVScrollSpeed = 0.10f;
        protected bool m_CaretVisible;
        private Coroutine m_BlinkCoroutine = null;
        private float m_BlinkStartTime = 0.0f;
        private Coroutine m_DragCoroutine = null;
        private string m_OriginalText = "";
        private bool m_WasCanceled = false;
        private bool m_HasDoneFocusTransition = false;
        private WaitForSecondsRealtime m_WaitForSecondsRealtime;
        private bool m_PreventCallback = false;
        private bool m_TouchKeyboardAllowsInPlaceEditing = false;
        private bool m_IsTextComponentUpdateRequired = false;
        private bool m_HasTextBeenRemoved = false;
        private float m_PointerDownClickStartTime;
        private float m_KeyDownStartTime;
        private float m_DoubleClickDelay = 0.5f;
        private bool m_IsApplePlatform = false;
        // Doesn't include dot and @ on purpose! See usage for details.
        const string kEmailSpecialCharacters = "!#$%&'*+-/=?^_`{|}~";
        const string kOculusQuestDeviceModel = "Oculus Quest";
        private BaseInput inputSystem
        {
            get
            {
                if (EventSystem.current && EventSystem.current.currentInputModule)
                    return EventSystem.current.currentInputModule.input;
                return null;
            }
        }
        private string compositionString
        {
            get { return inputSystem != null ? inputSystem.compositionString : Input.compositionString; }
        }
        private bool m_IsCompositionActive = false;
        private bool m_ShouldUpdateIMEWindowPosition = false;
        private int m_PreviousIMEInsertionLine = 0;
        private int compositionLength
        {
            get
            {
                if (m_ReadOnly)
                    return 0;
                return compositionString.Length;
            }
        }
        protected TMP_InputField()
        {
            SetTextComponentWrapMode();
        }
        protected Mesh mesh
        {
            get
            {
                if (m_Mesh == null)
                    m_Mesh = new Mesh();
                return m_Mesh;
            }
        }
        /// 
        /// Should the inputfield be automatically activated upon selection.
        /// 
        public virtual bool shouldActivateOnSelect
        {
            set
            {
                m_ShouldActivateOnSelect = value;
            }
            get
            {
                return m_ShouldActivateOnSelect && Application.platform != RuntimePlatform.tvOS;
            }
        }
        /// 
        /// Should the mobile keyboard input be hidden.
        /// 
        public bool shouldHideMobileInput
        {
            get
            {
                switch (Application.platform)
                {
                    case RuntimePlatform.Android:
                    case RuntimePlatform.IPhonePlayer:
                    case RuntimePlatform.tvOS:
                    #if UNITY_2022_1_OR_NEWER
                    case RuntimePlatform.WebGLPlayer:
                    #endif
                        return m_HideMobileInput;
                    default:
                        return true;
                }
            }
            set
            {
                switch(Application.platform)
                {
                    case RuntimePlatform.Android:
                    case RuntimePlatform.IPhonePlayer:
                    case RuntimePlatform.tvOS:
                    #if UNITY_2022_1_OR_NEWER
                    case RuntimePlatform.WebGLPlayer:
                    #endif
                        SetPropertyUtility.SetStruct(ref m_HideMobileInput, value);
                        break;
                    default:
                        m_HideMobileInput = true;
                        break;
                }
            }
        }
        public bool shouldHideSoftKeyboard
        {
            get
            {
                switch (Application.platform)
                {
                    case RuntimePlatform.Android:
                    case RuntimePlatform.IPhonePlayer:
                    case RuntimePlatform.tvOS:
                    #if UNITY_XR_VISIONOS_SUPPORTED
                    case RuntimePlatform.VisionOS:
                    #endif
                    case RuntimePlatform.WSAPlayerX86:
                    case RuntimePlatform.WSAPlayerX64:
                    case RuntimePlatform.WSAPlayerARM:
                    #if UNITY_2020_2_OR_NEWER
                    case RuntimePlatform.PS4:
                        #if !(UNITY_2020_2_1 || UNITY_2020_2_2)
                        case RuntimePlatform.PS5:
                        #endif
                    #endif
                    #if UNITY_2019_4_OR_NEWER
                    case RuntimePlatform.GameCoreXboxOne:
                    case RuntimePlatform.GameCoreXboxSeries:
                    #endif
                    case RuntimePlatform.Switch:
                    #if UNITY_2022_1_OR_NEWER
                    case RuntimePlatform.WebGLPlayer:
                    #endif
                        return m_HideSoftKeyboard;
                    default:
                        return true;
                }
            }
            set
            {
                switch (Application.platform)
                {
                    case RuntimePlatform.Android:
                    case RuntimePlatform.IPhonePlayer:
                    case RuntimePlatform.tvOS:
                    #if UNITY_XR_VISIONOS_SUPPORTED
                    case RuntimePlatform.VisionOS:
                    #endif
                    case RuntimePlatform.WSAPlayerX86:
                    case RuntimePlatform.WSAPlayerX64:
                    case RuntimePlatform.WSAPlayerARM:
                    #if UNITY_2020_2_OR_NEWER
                    case RuntimePlatform.PS4:
                        #if !(UNITY_2020_2_1 || UNITY_2020_2_2)
                        case RuntimePlatform.PS5:
                        #endif
                    #endif
                    #if UNITY_2019_4_OR_NEWER
                    case RuntimePlatform.GameCoreXboxOne:
                    case RuntimePlatform.GameCoreXboxSeries:
                    #endif
                    case RuntimePlatform.Switch:
                    #if UNITY_2022_1_OR_NEWER
                    case RuntimePlatform.WebGLPlayer:
                    #endif
                        SetPropertyUtility.SetStruct(ref m_HideSoftKeyboard, value);
                        break;
                    default:
                        m_HideSoftKeyboard = true;
                        break;
                }
                if (m_HideSoftKeyboard == true && m_SoftKeyboard != null && TouchScreenKeyboard.isSupported && m_SoftKeyboard.active)
                {
                    m_SoftKeyboard.active = false;
                    m_SoftKeyboard = null;
                }
            }
        }
        private bool isKeyboardUsingEvents()
        {
            switch (Application.platform)
            {
                case RuntimePlatform.Android:
                    return InPlaceEditing() && m_HideSoftKeyboard;
                case RuntimePlatform.IPhonePlayer:
                case RuntimePlatform.tvOS:
                #if UNITY_XR_VISIONOS_SUPPORTED
                case RuntimePlatform.VisionOS:
                #endif
                    return m_HideSoftKeyboard;
                #if UNITY_2020_2_OR_NEWER
                case RuntimePlatform.PS4:
                    #if !(UNITY_2020_2_1 || UNITY_2020_2_2)
                    case RuntimePlatform.PS5:
                    #endif
                #endif
                #if UNITY_2019_4_OR_NEWER
                case RuntimePlatform.GameCoreXboxOne:
                case RuntimePlatform.GameCoreXboxSeries:
                #endif
                case RuntimePlatform.Switch:
                    return false;
                #if UNITY_2022_1_OR_NEWER
                case RuntimePlatform.WebGLPlayer:
                    return m_SoftKeyboard == null || !m_SoftKeyboard.active;
                #endif
                default:
                    return true;
            }
        }
        private bool isUWP()
        {
            return Application.platform == RuntimePlatform.WSAPlayerX86 || Application.platform == RuntimePlatform.WSAPlayerX64 || Application.platform == RuntimePlatform.WSAPlayerARM;
        }
        /// 
        /// Input field's current text value. This is not necessarily the same as what is visible on screen.
        /// 
        /// 
        /// Note that null is invalid value  for InputField.text.
        /// 
        /// 
        /// 
        /// using UnityEngine;
        /// using System.Collections;
        /// using UnityEngine.UI; // Required when Using UI elements.
        ///
        /// public class Example : MonoBehaviour
        /// {
        ///     public InputField mainInputField;
        ///
        ///     public void Start()
        ///     {
        ///         mainInputField.text = "Enter Text Here...";
        ///     }
        /// }
        /// 
        /// 
        public string text
        {
            get
            {
                return m_Text;
            }
            set
            {
                SetText(value);
            }
        }
        /// 
        /// Set Input field's current text value without invoke onValueChanged. This is not necessarily the same as what is visible on screen.
        /// 
        public void SetTextWithoutNotify(string input)
        {
            SetText(input, false);
        }
        void SetText(string value, bool sendCallback = true)
        {
            if (this.text == value)
                return;
            if (value == null)
                value = "";
            value = value.Replace("\0", string.Empty); // remove embedded nulls
            m_Text = value;
            /*
            if (m_LineType == LineType.SingleLine)
                value = value.Replace("\n", "").Replace("\t", "");
            // If we have an input validator, validate the input and apply the character limit at the same time.
            if (onValidateInput != null || characterValidation != CharacterValidation.None)
            {
                m_Text = "";
                OnValidateInput validatorMethod = onValidateInput ?? Validate;
                m_CaretPosition = m_CaretSelectPosition = value.Length;
                int charactersToCheck = characterLimit > 0 ? Math.Min(characterLimit, value.Length) : value.Length;
                for (int i = 0; i < charactersToCheck; ++i)
                {
                    char c = validatorMethod(m_Text, m_Text.Length, value[i]);
                    if (c != 0)
                        m_Text += c;
                }
            }
            else
            {
                m_Text = characterLimit > 0 && value.Length > characterLimit ? value.Substring(0, characterLimit) : value;
            }
            */
            #if UNITY_EDITOR
            if (!Application.isPlaying)
            {
                SendOnValueChangedAndUpdateLabel();
                return;
            }
            #endif
            if (m_SoftKeyboard != null)
                m_SoftKeyboard.text = m_Text;
            if (m_StringPosition > m_Text.Length)
                m_StringPosition = m_StringSelectPosition = m_Text.Length;
            else if (m_StringSelectPosition > m_Text.Length)
                m_StringSelectPosition = m_Text.Length;
            m_forceRectTransformAdjustment = true;
            m_IsTextComponentUpdateRequired = true;
            UpdateLabel();
            if (sendCallback)
                SendOnValueChanged();
        }
        public bool isFocused
        {
            get { return m_AllowInput; }
        }
        public float caretBlinkRate
        {
            get { return m_CaretBlinkRate; }
            set
            {
                if (SetPropertyUtility.SetStruct(ref m_CaretBlinkRate, value))
                {
                    if (m_AllowInput)
                        SetCaretActive();
                }
            }
        }
        public int caretWidth { get { return m_CaretWidth; } set { if (SetPropertyUtility.SetStruct(ref m_CaretWidth, value)) MarkGeometryAsDirty(); } }
        public RectTransform textViewport { get { return m_TextViewport; } set { SetPropertyUtility.SetClass(ref m_TextViewport, value); } }
        public TMP_Text textComponent
        {
            get { return m_TextComponent; }
            set
            {
                if (SetPropertyUtility.SetClass(ref m_TextComponent, value))
                {
                    SetTextComponentWrapMode();
                }
            }
        }
        //public TMP_Text placeholderTextComponent { get { return m_PlaceholderTextComponent; } set { SetPropertyUtility.SetClass(ref m_PlaceholderTextComponent, value); } }
        public Graphic placeholder { get { return m_Placeholder; } set { SetPropertyUtility.SetClass(ref m_Placeholder, value); } }
        public Scrollbar verticalScrollbar
        {
            get { return m_VerticalScrollbar; }
            set
            {
                if (m_VerticalScrollbar != null)
                    m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange);
                SetPropertyUtility.SetClass(ref m_VerticalScrollbar, value);
                if (m_VerticalScrollbar)
                {
                    m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange);
                }
            }
        }
        public float scrollSensitivity { get { return m_ScrollSensitivity; } set { if (SetPropertyUtility.SetStruct(ref m_ScrollSensitivity, value)) MarkGeometryAsDirty(); } }
        public Color caretColor { get { return customCaretColor ? m_CaretColor : textComponent.color; } set { if (SetPropertyUtility.SetColor(ref m_CaretColor, value)) MarkGeometryAsDirty(); } }
        public bool customCaretColor { get { return m_CustomCaretColor; } set { if (m_CustomCaretColor != value) { m_CustomCaretColor = value; MarkGeometryAsDirty(); } } }
        public Color selectionColor { get { return m_SelectionColor; } set { if (SetPropertyUtility.SetColor(ref m_SelectionColor, value)) MarkGeometryAsDirty(); } }
        public SubmitEvent onEndEdit { get { return m_OnEndEdit; } set { SetPropertyUtility.SetClass(ref m_OnEndEdit, value); } }
        public SubmitEvent onSubmit { get { return m_OnSubmit; } set { SetPropertyUtility.SetClass(ref m_OnSubmit, value); } }
        public SelectionEvent onSelect { get { return m_OnSelect; } set { SetPropertyUtility.SetClass(ref m_OnSelect, value); } }
        public SelectionEvent onDeselect { get { return m_OnDeselect; } set { SetPropertyUtility.SetClass(ref m_OnDeselect, value); } }
        public TextSelectionEvent onTextSelection { get { return m_OnTextSelection; } set { SetPropertyUtility.SetClass(ref m_OnTextSelection, value); } }
        public TextSelectionEvent onEndTextSelection { get { return m_OnEndTextSelection; } set { SetPropertyUtility.SetClass(ref m_OnEndTextSelection, value); } }
        public OnChangeEvent onValueChanged { get { return m_OnValueChanged; } set { SetPropertyUtility.SetClass(ref m_OnValueChanged, value); } }
        public TouchScreenKeyboardEvent onTouchScreenKeyboardStatusChanged { get { return m_OnTouchScreenKeyboardStatusChanged; } set { SetPropertyUtility.SetClass(ref m_OnTouchScreenKeyboardStatusChanged, value); } }
        public OnValidateInput onValidateInput { get { return m_OnValidateInput; } set { SetPropertyUtility.SetClass(ref m_OnValidateInput, value); } }
        public int characterLimit
        {
            get { return m_CharacterLimit; }
            set
            {
                if (SetPropertyUtility.SetStruct(ref m_CharacterLimit, Math.Max(0, value)))
                {
                    UpdateLabel();
                    if (m_SoftKeyboard != null)
                        m_SoftKeyboard.characterLimit = value;
                }
            }
        }
        //public bool isInteractableControl { set { if ( } }
        /// 
        /// Set the point size on both Placeholder and Input text object.
        /// 
        public float pointSize
        {
            get { return m_GlobalPointSize; }
            set
            {
                if (SetPropertyUtility.SetStruct(ref m_GlobalPointSize, Math.Max(0, value)))
                {
                    SetGlobalPointSize(m_GlobalPointSize);
                    UpdateLabel();
                }
            }
        }
        /// 
        /// Sets the Font Asset on both Placeholder and Input child objects.
        /// 
        public TMP_FontAsset fontAsset
        {
            get { return m_GlobalFontAsset; }
            set
            {
                if (SetPropertyUtility.SetClass(ref m_GlobalFontAsset, value))
                {
                    SetGlobalFontAsset(m_GlobalFontAsset);
                    UpdateLabel();
                }
            }
        }
        [SerializeField]
        protected TMP_FontAsset m_GlobalFontAsset;
        /// 
        /// Determines if the whole text will be selected when focused.
        /// 
        public bool onFocusSelectAll
        {
            get { return m_OnFocusSelectAll; }
            set { m_OnFocusSelectAll = value; }
        }
        [SerializeField]
        protected bool m_OnFocusSelectAll = true;
        protected bool m_isSelectAll;
        /// 
        /// Determines if the text and caret position as well as selection will be reset when the input field is deactivated.
        /// 
        public bool resetOnDeActivation
        {
            get { return m_ResetOnDeActivation; }
            set { m_ResetOnDeActivation = value; }
        }
        [SerializeField]
        protected bool m_ResetOnDeActivation = true;
        private bool m_SelectionStillActive = false;
        private bool m_ReleaseSelection = false;
        private KeyCode m_LastKeyCode;
        private GameObject m_PreviouslySelectedObject;
        /// 
        /// Determines if the text selection will remain visible when the input field looses focus and is deactivated.
        /// 
        public bool keepTextSelectionVisible
        {
            get { return m_KeepTextSelectionVisible; }
            set { m_KeepTextSelectionVisible = value; }
        }
        [SerializeField]
        private bool m_KeepTextSelectionVisible;
        /// 
        /// Controls whether the original text is restored when pressing "ESC".
        /// 
        public bool restoreOriginalTextOnEscape
        {
            get { return m_RestoreOriginalTextOnEscape; }
            set { m_RestoreOriginalTextOnEscape = value; }
        }
        [SerializeField]
        private bool m_RestoreOriginalTextOnEscape = true;
        /// 
        /// Is Rich Text editing allowed?
        /// 
        public bool isRichTextEditingAllowed
        {
            get { return m_isRichTextEditingAllowed; }
            set { m_isRichTextEditingAllowed = value; }
        }
        [SerializeField]
        protected bool m_isRichTextEditingAllowed = false;
        // Content Type related
        public ContentType contentType { get { return m_ContentType; } set { if (SetPropertyUtility.SetStruct(ref m_ContentType, value)) EnforceContentType(); } }
        public LineType lineType
        {
            get { return m_LineType; }
            set
            {
                if (SetPropertyUtility.SetStruct(ref m_LineType, value))
                {
                    SetToCustomIfContentTypeIsNot(ContentType.Standard, ContentType.Autocorrected);
                    SetTextComponentWrapMode();
                }
            }
        }
        /// 
        /// Limits the number of lines of text in the Input Field.
        /// 
        public int lineLimit
        {
            get { return m_LineLimit; }
            set
            {
                if (m_LineType == LineType.SingleLine)
                    m_LineLimit = 1;
                else
                    SetPropertyUtility.SetStruct(ref m_LineLimit, value);
            }
        }
        [SerializeField]
        protected int m_LineLimit = 0;
        /// 
        /// The type of input expected. See InputField.InputType.
        /// 
        public InputType inputType { get { return m_InputType; } set { if (SetPropertyUtility.SetStruct(ref m_InputType, value)) SetToCustom(); } }
        /// 
        /// The TouchScreenKeyboard being used to edit the Input Field.
        /// 
        public TouchScreenKeyboard touchScreenKeyboard { get { return m_SoftKeyboard; } }
        /// 
        /// They type of mobile keyboard that will be used.
        /// 
        public TouchScreenKeyboardType keyboardType
        {
            get { return m_KeyboardType; }
            set
            {
                if (SetPropertyUtility.SetStruct(ref m_KeyboardType, value))
                    SetToCustom();
            }
        }
        /// 
        /// Determines if the keyboard is opened in alert mode.
        /// 
        public bool isAlert;
        /// 
        /// The type of validation to perform on a character
        /// 
        public CharacterValidation characterValidation { get { return m_CharacterValidation; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterValidation, value)) SetToCustom(); } }
        /// 
        /// Sets the Input Validation to use a Custom Input Validation script.
        /// 
        public TMP_InputValidator inputValidator
        {
            get { return m_InputValidator; }
            set {  if (SetPropertyUtility.SetClass(ref m_InputValidator, value)) SetToCustom(CharacterValidation.CustomValidator); }
        }
        [SerializeField]
        protected TMP_InputValidator m_InputValidator = null;
        public bool readOnly { get { return m_ReadOnly; } set { m_ReadOnly = value; } }
        [SerializeField]
        private bool m_ShouldActivateOnSelect = true;
        public bool richText { get { return m_RichText; } set { m_RichText = value; SetTextComponentRichTextMode(); } }
        // Derived property
        public bool multiLine { get { return m_LineType == LineType.MultiLineNewline || lineType == LineType.MultiLineSubmit; } }
        // Not shown in Inspector.
        public char asteriskChar { get { return m_AsteriskChar; } set { if (SetPropertyUtility.SetStruct(ref m_AsteriskChar, value)) UpdateLabel(); } }
        public bool wasCanceled { get { return m_WasCanceled; } }
        protected void ClampStringPos(ref int pos)
        {
            if (pos <= 0)
                pos = 0;
            else if (pos > text.Length)
                pos = text.Length;
        }
        protected void ClampCaretPos(ref int pos)
        {
            if (pos > m_TextComponent.textInfo.characterCount - 1)
                pos = m_TextComponent.textInfo.characterCount - 1;
            if (pos <= 0)
                pos = 0;
        }
        int ClampArrayIndex(int index)
        {
            if (index < 0)
                return 0;
            return index;
        }
        /// 
        /// Current position of the cursor.
        /// Getters are public Setters are protected
        /// 
        protected int caretPositionInternal { get { return m_CaretPosition + compositionLength; } set { m_CaretPosition = value; ClampCaretPos(ref m_CaretPosition); } }
        protected int stringPositionInternal { get { return m_StringPosition + compositionLength; } set { m_StringPosition = value; ClampStringPos(ref m_StringPosition); } }
        protected int caretSelectPositionInternal { get { return m_CaretSelectPosition + compositionLength; } set { m_CaretSelectPosition = value; ClampCaretPos(ref m_CaretSelectPosition); } }
        protected int stringSelectPositionInternal { get { return m_StringSelectPosition + compositionLength; } set { m_StringSelectPosition = value; ClampStringPos(ref m_StringSelectPosition); } }
        private bool hasSelection { get { return stringPositionInternal != stringSelectPositionInternal; } }
        private bool m_isSelected;
        private bool m_IsStringPositionDirty;
        private bool m_IsCaretPositionDirty;
        private bool m_forceRectTransformAdjustment;
		// Primary to track when an user presses on the X to close the keyboard in the HoloLens
		private bool m_IsKeyboardBeingClosedInHoloLens = false;
        /// 
        /// Get: Returns the focus position as thats the position that moves around even during selection.
        /// Set: Set both the anchor and focus position such that a selection doesn't happen
        /// 
        public int caretPosition
        {
            get => caretSelectPositionInternal;
            set { selectionAnchorPosition = value; selectionFocusPosition = value; UpdateStringIndexFromCaretPosition(); }
        }
        /// 
        /// Get: Returns the fixed position of selection
        /// Set: If compositionString is 0 set the fixed position
        /// 
        public int selectionAnchorPosition
        {
            get
            {
                return caretPositionInternal;
            }
            set
            {
                if (compositionLength != 0)
                    return;
                caretPositionInternal = value;
                m_IsStringPositionDirty = true;
            }
        }
        /// 
        /// Get: Returns the variable position of selection
        /// Set: If compositionString is 0 set the variable position
        /// 
        public int selectionFocusPosition
        {
            get
            {
                return caretSelectPositionInternal;
            }
            set
            {
                if (compositionLength != 0)
                    return;
                caretSelectPositionInternal = value;
                m_IsStringPositionDirty = true;
            }
        }
        /// 
        ///
        /// 
        public int stringPosition
        {
            get => stringSelectPositionInternal;
            set { selectionStringAnchorPosition = value; selectionStringFocusPosition = value; UpdateCaretPositionFromStringIndex(); }
        }
        /// 
        /// The fixed position of the selection in the raw string which may contains rich text.
        /// 
        public int selectionStringAnchorPosition
        {
            get
            {
                return stringPositionInternal;
            }
            set
            {
                if (compositionLength != 0)
                    return;
                stringPositionInternal = value;
                m_IsCaretPositionDirty = true;
            }
        }
        /// 
        /// The variable position of the selection in the raw string which may contains rich text.
        /// 
        public int selectionStringFocusPosition
        {
            get
            {
                return stringSelectPositionInternal;
            }
            set
            {
                if (compositionLength != 0)
                    return;
                stringSelectPositionInternal = value;
                m_IsCaretPositionDirty = true;
            }
        }
        #if UNITY_EDITOR
        // Remember: This is NOT related to text validation!
        // This is Unity's own OnValidate method which is invoked when changing values in the Inspector.
        protected override void OnValidate()
        {
            base.OnValidate();
            EnforceContentType();
            m_CharacterLimit = Math.Max(0, m_CharacterLimit);
            //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
            if (!IsActive())
                return;
            SetTextComponentRichTextMode();
            UpdateLabel();
            if (m_AllowInput)
                SetCaretActive();
        }
        #endif // if UNITY_EDITOR
    	#if UNITY_ANDROID
        protected override void Awake()
        {
            base.Awake();
            if (s_IsQuestDeviceEvaluated)
                return;
            // Used for Oculus Quest 1 and 2 software keyboard regression.
            // TouchScreenKeyboard.isInPlaceEditingAllowed is always returning true in these devices and would prevent the software keyboard from showing up if that value was used.
            s_IsQuestDevice = SystemInfo.deviceModel == kOculusQuestDeviceModel;
            s_IsQuestDeviceEvaluated = true;
        }
    	#endif // if UNITY_ANDROID
        protected override void OnEnable()
        {
            //Debug.Log("*** OnEnable() *** - " + this.name);
            base.OnEnable();
            if (m_Text == null)
                m_Text = string.Empty;
            m_IsApplePlatform = SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX || SystemInfo.operatingSystem.Contains("iOS") || SystemInfo.operatingSystem.Contains("tvOS");
            // Check if Input Field is driven by any layout components
            ILayoutController layoutController = GetComponent();
            if (layoutController != null)
            {
                m_IsDrivenByLayoutComponents = true;
                m_LayoutGroup = GetComponent();
            }
            else
                m_IsDrivenByLayoutComponents = false;
            if (Application.isPlaying)
            {
                if (m_CachedInputRenderer == null && m_TextComponent != null)
                {
                    GameObject go = new GameObject("Caret", typeof(TMP_SelectionCaret));
                    go.hideFlags = HideFlags.DontSave;
                    go.transform.SetParent(m_TextComponent.transform.parent);
                    go.transform.SetAsFirstSibling();
                    go.layer = gameObject.layer;
                    caretRectTrans = go.GetComponent();
                    m_CachedInputRenderer = go.GetComponent();
                    m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
                    // Needed as if any layout is present we want the caret to always be the same as the text area.
                    go.AddComponent().ignoreLayout = true;
                    AssignPositioningIfNeeded();
                }
            }
            m_RectTransform = GetComponent();
            // Check if parent component has IScrollHandler
            IScrollHandler[] scrollHandlers = GetComponentsInParent();
            if (scrollHandlers.Length > 1)
                m_IScrollHandlerParent = scrollHandlers[1] as ScrollRect;
            // Get a reference to the RectMask 2D on the Viewport Text Area object.
            if (m_TextViewport != null)
            {
                m_TextViewportRectMask = m_TextViewport.GetComponent();
                UpdateMaskRegions();
            }
            // If we have a cached renderer then we had OnDisable called so just restore the material.
            if (m_CachedInputRenderer != null)
                m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
            if (m_TextComponent != null)
            {
                m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty);
                m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel);
                // Cache reference to Vertical Scrollbar RectTransform and add listener.
                if (m_VerticalScrollbar != null)
                {
                    m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange);
                }
                UpdateLabel();
            }
            #if UNITY_2019_1_OR_NEWER
            m_TouchKeyboardAllowsInPlaceEditing = TouchScreenKeyboard.isInPlaceEditingAllowed;
            #endif
            // Subscribe to event fired when text object has been regenerated.
            TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
        }
        protected override void OnDisable()
        {
            // the coroutine will be terminated, so this will ensure it restarts when we are next activated
            m_BlinkCoroutine = null;
            DeactivateInputField();
            if (m_TextComponent != null)
            {
                m_TextComponent.UnregisterDirtyVerticesCallback(MarkGeometryAsDirty);
                m_TextComponent.UnregisterDirtyVerticesCallback(UpdateLabel);
                if (m_VerticalScrollbar != null)
                    m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange);
            }
            CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
            // Clear needs to be called otherwise sync never happens as the object is disabled.
            if (m_CachedInputRenderer != null)
                m_CachedInputRenderer.Clear();
            if (m_Mesh != null)
                DestroyImmediate(m_Mesh);
            m_Mesh = null;
            // Unsubscribe to event triggered when text object has been regenerated
            TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
            base.OnDisable();
        }
        /// 
        /// Method used to update the tracking of the caret position when the text object has been regenerated.
        /// 
        /// 
        private void ON_TEXT_CHANGED(UnityEngine.Object obj)
        {
            bool isThisObject = obj == m_TextComponent;
            if (isThisObject && !m_IsStringPositionDirty)
            {
                if (Application.isPlaying && compositionLength == 0)
                {
                    UpdateCaretPositionFromStringIndex();
                    #if TMP_DEBUG_MODE
                    Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
                    #endif
                }
                if (m_VerticalScrollbar)
                    UpdateScrollbar();
            }
        }
        IEnumerator CaretBlink()
        {
            // Always ensure caret is initially visible since it can otherwise be confusing for a moment.
            m_CaretVisible = true;
            yield return null;
            while ((isFocused || m_SelectionStillActive) && m_CaretBlinkRate > 0)
            {
                // the blink rate is expressed as a frequency
                float blinkPeriod = 1f / m_CaretBlinkRate;
                // the caret should be ON if we are in the first half of the blink period
                bool blinkState = (Time.unscaledTime - m_BlinkStartTime) % blinkPeriod < blinkPeriod / 2;
                if (m_CaretVisible != blinkState)
                {
                    m_CaretVisible = blinkState;
                    if (!hasSelection)
                        MarkGeometryAsDirty();
                }
                // Then wait again.
                yield return null;
            }
            m_BlinkCoroutine = null;
        }
        void SetCaretVisible()
        {
            if (!m_AllowInput)
                return;
            m_CaretVisible = true;
            m_BlinkStartTime = Time.unscaledTime;
            SetCaretActive();
        }
        // SetCaretActive will not set the caret immediately visible - it will wait for the next time to blink.
        // However, it will handle things correctly if the blink speed changed from zero to non-zero or non-zero to zero.
        void SetCaretActive()
        {
            if (!m_AllowInput)
                return;
            if (m_CaretBlinkRate > 0.0f)
            {
                if (m_BlinkCoroutine == null)
                    m_BlinkCoroutine = StartCoroutine(CaretBlink());
            }
            else
            {
                m_CaretVisible = true;
            }
        }
        protected void OnFocus()
        {
            if (m_OnFocusSelectAll)
                SelectAll();
        }
        protected void SelectAll()
        {
            m_isSelectAll = true;
            stringPositionInternal = text.Length;
            stringSelectPositionInternal = 0;
        }
        /// 
        /// Move to the end of the text.
        /// 
        /// 
        public void MoveTextEnd(bool shift)
        {
            if (m_isRichTextEditingAllowed)
            {
                int position = text.Length;
                if (shift)
                {
                    stringSelectPositionInternal = position;
                }
                else
                {
                    stringPositionInternal = position;
                    stringSelectPositionInternal = stringPositionInternal;
                }
            }
            else
            {
                int position = m_TextComponent.textInfo.characterCount - 1;
                if (shift)
                {
                    caretSelectPositionInternal = position;
                    stringSelectPositionInternal = GetStringIndexFromCaretPosition(position);
                }
                else
                {
                    caretPositionInternal = caretSelectPositionInternal = position;
                    stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(position);
                }
            }
            UpdateLabel();
        }
        /// 
        /// Move to the start of the text.
        /// 
        /// 
        public void MoveTextStart(bool shift)
        {
            if (m_isRichTextEditingAllowed)
            {
                int position = 0;
                if (shift)
                {
                    stringSelectPositionInternal = position;
                }
                else
                {
                    stringPositionInternal = position;
                    stringSelectPositionInternal = stringPositionInternal;
                }
            }
            else
            {
                int position = 0;
                if (shift)
                {
                    caretSelectPositionInternal = position;
                    stringSelectPositionInternal = GetStringIndexFromCaretPosition(position);
                }
                else
                {
                    caretPositionInternal = caretSelectPositionInternal = position;
                    stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(position);
                }
            }
            UpdateLabel();
        }
        /// 
        /// Move to the end of the current line of text.
        /// 
        /// 
        public void MoveToEndOfLine(bool shift, bool ctrl)
        {
            // Get the line the caret is currently located on.
            int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
            // Get the last character of the given line.
            int characterIndex = ctrl == true ? m_TextComponent.textInfo.characterCount - 1 : m_TextComponent.textInfo.lineInfo[currentLine].lastCharacterIndex;
            int position = m_TextComponent.textInfo.characterInfo[characterIndex].index;
            if (shift)
            {
                stringSelectPositionInternal = position;
                caretSelectPositionInternal = characterIndex;
            }
            else
            {
                stringPositionInternal = position;
                stringSelectPositionInternal = stringPositionInternal;
                caretSelectPositionInternal = caretPositionInternal = characterIndex;
            }
            UpdateLabel();
        }
        /// 
        /// Move to the start of the current line of text.
        /// 
        /// 
        public void MoveToStartOfLine(bool shift, bool ctrl)
        {
            // Get the line the caret is currently located on.
            int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
            // Get the first character of the given line.
            int characterIndex = ctrl == true ? 0 : m_TextComponent.textInfo.lineInfo[currentLine].firstCharacterIndex;
            int position = 0;
            if (characterIndex > 0)
                position = m_TextComponent.textInfo.characterInfo[characterIndex - 1].index + m_TextComponent.textInfo.characterInfo[characterIndex - 1].stringLength;
            if (shift)
            {
                stringSelectPositionInternal = position;
                caretSelectPositionInternal = characterIndex;
            }
            else
            {
                stringPositionInternal = position;
                stringSelectPositionInternal = stringPositionInternal;
                caretSelectPositionInternal = caretPositionInternal = characterIndex;
            }
            UpdateLabel();
        }
        static string clipboard
        {
            get
            {
                return GUIUtility.systemCopyBuffer;
            }
            set
            {
                GUIUtility.systemCopyBuffer = value;
            }
        }
        private bool InPlaceEditing()
        {
            if (m_TouchKeyboardAllowsInPlaceEditing)
                return true;
            if (isUWP())
                return !TouchScreenKeyboard.isSupported;
            if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard)
                return true;
            if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard == false && shouldHideMobileInput == false)
                return false;
            return true;
        }
        // In-place editing can change state if a hardware keyboard becomes available or is hidden while the input field is activated.
        private bool InPlaceEditingChanged()
        {
                return !s_IsQuestDevice && m_TouchKeyboardAllowsInPlaceEditing != TouchScreenKeyboard.isInPlaceEditingAllowed;
        }
        // Returns true if the TouchScreenKeyboard should be used. On Android and Chrome OS, we only want to use the
        // TouchScreenKeyboard if in-place editing is not allowed (i.e. when we do not have a hardware keyboard available).
        private bool TouchScreenKeyboardShouldBeUsed()
        {
            RuntimePlatform platform = Application.platform;
            switch (platform)
            {
                case RuntimePlatform.Android:
                case RuntimePlatform.WebGLPlayer:
                    if (s_IsQuestDevice)
                        return TouchScreenKeyboard.isSupported;
                    return !TouchScreenKeyboard.isInPlaceEditingAllowed;
                default:
                    return TouchScreenKeyboard.isSupported;
            }
        }
        void UpdateKeyboardStringPosition()
        {
            // On iOS/tvOS we only update SoftKeyboard selection when we know that it might have changed by touch/pointer interactions with InputField
            // Setting the TouchScreenKeyboard selection here instead of LateUpdate so that we wouldn't override
            // TouchScreenKeyboard selection when it's changed with cmd+a/ctrl+a/arrow/etc. in the TouchScreenKeyboard
            // This is only applicable for iOS/tvOS as we have instance of TouchScreenKeyboard even when external keyboard is connected
            if (m_HideMobileInput && m_SoftKeyboard != null && m_SoftKeyboard.canSetSelection &&
                (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.tvOS))
            {
                var selectionStart = Mathf.Min(stringSelectPositionInternal, stringPositionInternal);
                var selectionLength = Mathf.Abs(stringSelectPositionInternal - stringPositionInternal);
                m_SoftKeyboard.selection = new RangeInt(selectionStart, selectionLength);
            }
        }
        void UpdateStringPositionFromKeyboard()
        {
            // TODO: Might want to add null check here.
            var selectionRange = m_SoftKeyboard.selection;
            //if (selectionRange.start == 0 && selectionRange.length == 0)
            //    return;
            var selectionStart = selectionRange.start;
            var selectionEnd = selectionRange.end;
            var stringPositionChanged = false;
            if (stringPositionInternal != selectionStart)
            {
                stringPositionChanged = true;
                stringPositionInternal = selectionStart;
                caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
            }
            if (stringSelectPositionInternal != selectionEnd)
            {
                stringSelectPositionInternal = selectionEnd;
                stringPositionChanged = true;
                caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            }
            if (stringPositionChanged)
            {
                m_BlinkStartTime = Time.unscaledTime;
                UpdateLabel();
            }
        }
        /// 
        /// Update the text based on input.
        /// 
        // TODO: Make LateUpdate a coroutine instead. Allows us to control the update to only be when the field is active.
        protected virtual void LateUpdate()
        {
            // Only activate if we are not already activated.
            if (m_ShouldActivateNextUpdate)
            {
                if (!isFocused)
                {
                    ActivateInputFieldInternal();
                    m_ShouldActivateNextUpdate = false;
                    return;
                }
                // Reset as we are already activated.
                m_ShouldActivateNextUpdate = false;
            }
            // If the device's state changed in a way that affects whether we should use a touchscreen keyboard or not,
            // then deactivate the input field.
            if (isFocused && InPlaceEditingChanged())
                DeactivateInputField();
            // Handle double click to reset / deselect Input Field when ResetOnActivation is false.
            if (!isFocused && m_SelectionStillActive)
            {
                GameObject selectedObject = EventSystem.current != null ? EventSystem.current.currentSelectedGameObject : null;
                if (selectedObject == null && m_ResetOnDeActivation)
                {
                    ReleaseSelection();
                    return;
                }
                if (selectedObject != null && selectedObject != this.gameObject)
                {
                    if (selectedObject == m_PreviouslySelectedObject)
                        return;
                    m_PreviouslySelectedObject = selectedObject;
                    // Special handling for Vertical Scrollbar
                    if (m_VerticalScrollbar && selectedObject == m_VerticalScrollbar.gameObject)
                    {
                        // Do not release selection
                        return;
                    }
                    // Release selection for all objects when ResetOnDeActivation is true
                    if (m_ResetOnDeActivation)
                    {
                        ReleaseSelection();
                        return;
                    }
                    // Release current selection of selected object is another Input Field
                    if (m_KeepTextSelectionVisible == false && selectedObject.GetComponent() != null)
                        ReleaseSelection();
                    return;
                }
                #if ENABLE_INPUT_SYSTEM
                if (m_ProcessingEvent != null && m_ProcessingEvent.rawType == EventType.MouseDown && m_ProcessingEvent.button == 0)
                {
                    // Check for Double Click
                    bool isDoubleClick = false;
                    float timeStamp = Time.unscaledTime;
                    if (m_KeyDownStartTime + m_DoubleClickDelay > timeStamp)
                        isDoubleClick = true;
                    m_KeyDownStartTime = timeStamp;
                    if (isDoubleClick)
                    {
                        //m_StringPosition = m_StringSelectPosition = 0;
                        //m_CaretPosition = m_CaretSelectPosition = 0;
                        //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
                        //if (caretRectTrans != null)
                        //    caretRectTrans.localPosition = Vector3.zero;
                        ReleaseSelection();
                        return;
                    }
                }
                #else
                if (Input.GetKeyDown(KeyCode.Mouse0))
                {
                    // Check for Double Click
                    bool isDoubleClick = false;
                    float timeStamp = Time.unscaledTime;
                    if (m_KeyDownStartTime + m_DoubleClickDelay > timeStamp)
                        isDoubleClick = true;
                    m_KeyDownStartTime = timeStamp;
                    if (isDoubleClick)
                    {
                        //m_StringPosition = m_StringSelectPosition = 0;
                        //m_CaretPosition = m_CaretSelectPosition = 0;
                        //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
                        //if (caretRectTrans != null)
                        //    caretRectTrans.localPosition = Vector3.zero;
                        ReleaseSelection();
                        return;
                    }
                }
                #endif
            }
            UpdateMaskRegions();
            if (InPlaceEditing() && isKeyboardUsingEvents() || !isFocused)
            {
                return;
            }
            AssignPositioningIfNeeded();
            if (m_SoftKeyboard == null || m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
            {
                if (m_SoftKeyboard != null)
                {
                    if (!m_ReadOnly)
                        text = m_SoftKeyboard.text;
                    TouchScreenKeyboard.Status status = m_SoftKeyboard.status;
                    // Special handling for UWP - Hololens which does not support Canceled status
                    if (m_LastKeyCode != KeyCode.Return && status == TouchScreenKeyboard.Status.Done && isUWP())
					{
                        status = TouchScreenKeyboard.Status.Canceled;
                        // The HoloLen's X button will not be acting as an ESC Key (TMBP-98)
						m_IsKeyboardBeingClosedInHoloLens = true;
					}
                    switch (status)
                    {
                        case TouchScreenKeyboard.Status.LostFocus:
                            SendTouchScreenKeyboardStatusChanged();
                            break;
                        case TouchScreenKeyboard.Status.Canceled:
                            m_ReleaseSelection = true;
                            m_WasCanceled = true;
                            SendTouchScreenKeyboardStatusChanged();
                            break;
                        case TouchScreenKeyboard.Status.Done:
                            m_ReleaseSelection = true;
                            SendTouchScreenKeyboardStatusChanged();
                            OnSubmit(null);
                            break;
                    }
                }
                OnDeselect(null);
                return;
            }
            string val = m_SoftKeyboard.text;
            if (m_Text != val)
            {
                if (m_ReadOnly)
                {
                    m_SoftKeyboard.text = m_Text;
                }
                else
                {
                    m_Text = "";
                    for (int i = 0; i < val.Length; ++i)
                    {
                        char c = val[i];
						bool hasValidateUpdatedText = false;
                        if (c == '\r' || c == 3)
                            c = '\n';
                        if (onValidateInput != null)
                            c = onValidateInput(m_Text, m_Text.Length, c);
                        else if (characterValidation != CharacterValidation.None)
						{
							string textBeforeValidate = m_Text;
                            c = Validate(m_Text, m_Text.Length, c);
                            hasValidateUpdatedText = textBeforeValidate != m_Text;
						}
                        if (lineType != LineType.MultiLineNewline && c == '\n')
                        {
                            UpdateLabel();
                            OnSubmit(null);
                            OnDeselect(null);
                            return;
                        }
                        // In the case of a Custom Validator, the user is expected to modify the m_Text where as such we do not append c.
                        // However we will append c if the user did not modify the m_Text (UUM-42147)
                        if (c != 0 && (characterValidation != CharacterValidation.CustomValidator || !hasValidateUpdatedText))
                            m_Text += c;
                    }
                    if (characterLimit > 0 && m_Text.Length > characterLimit)
                        m_Text = m_Text.Substring(0, characterLimit);
                    UpdateStringPositionFromKeyboard();
                    // Set keyboard text before updating label, as we might have changed it with validation
                    // and update label will take the old value from keyboard if we don't change it here
                    if (m_Text != val)
                        m_SoftKeyboard.text = m_Text;
                    SendOnValueChangedAndUpdateLabel();
                }
            }
            // On iOS/tvOS we always have TouchScreenKeyboard instance even when using external keyboard
            // so we keep track of the caret position there
            else if (m_HideMobileInput && m_SoftKeyboard != null && m_SoftKeyboard.canSetSelection &&
                     Application.platform != RuntimePlatform.IPhonePlayer && Application.platform != RuntimePlatform.tvOS)
            {
                var selectionStart = Mathf.Min(stringSelectPositionInternal, stringPositionInternal);
                var selectionLength = Mathf.Abs(stringSelectPositionInternal - stringPositionInternal);
                m_SoftKeyboard.selection = new RangeInt(selectionStart, selectionLength);
            }
            else if (m_HideMobileInput && Application.platform == RuntimePlatform.Android ||
                     m_SoftKeyboard.canSetSelection && (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.tvOS))
            {
                UpdateStringPositionFromKeyboard();
            }
            //else if (m_HideMobileInput) // m_Keyboard.canSetSelection
            //{
            //    int length = stringPositionInternal < stringSelectPositionInternal ? stringSelectPositionInternal - stringPositionInternal : stringPositionInternal - stringSelectPositionInternal;
            //    m_SoftKeyboard.selection = new RangeInt(stringPositionInternal < stringSelectPositionInternal ? stringPositionInternal : stringSelectPositionInternal, length);
            //}
            //else if (!m_HideMobileInput) // m_Keyboard.canGetSelection)
            //{
            //    UpdateStringPositionFromKeyboard();
            //}
            if (m_SoftKeyboard != null && m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
            {
                if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
                    m_WasCanceled = true;
                OnDeselect(null);
            }
        }
        private bool MayDrag(PointerEventData eventData)
        {
            return IsActive() &&
                   IsInteractable() &&
                   eventData.button == PointerEventData.InputButton.Left &&
                   m_TextComponent != null &&
                   (m_SoftKeyboard == null || shouldHideSoftKeyboard || shouldHideMobileInput);
        }
        public virtual void OnBeginDrag(PointerEventData eventData)
        {
            if (!MayDrag(eventData))
                return;
            m_UpdateDrag = true;
        }
        public virtual void OnDrag(PointerEventData eventData)
        {
            if (!MayDrag(eventData))
                return;
            CaretPosition insertionSide;
            int insertionIndex = TMP_TextUtilities.GetCursorIndexFromPosition(m_TextComponent, eventData.position, eventData.pressEventCamera, out insertionSide);
            if (m_isRichTextEditingAllowed)
            {
                if (insertionSide == CaretPosition.Left)
                {
                    stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
                }
                else if (insertionSide == CaretPosition.Right)
                {
                    stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                }
            }
            else
            {
                if (insertionSide == CaretPosition.Left)
                {
                    stringSelectPositionInternal = insertionIndex == 0
                        ? m_TextComponent.textInfo.characterInfo[0].index
                        : m_TextComponent.textInfo.characterInfo[insertionIndex - 1].index + m_TextComponent.textInfo.characterInfo[insertionIndex - 1].stringLength;
                }
                else if (insertionSide == CaretPosition.Right)
                {
                    stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                }
            }
            caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            MarkGeometryAsDirty();
            m_DragPositionOutOfBounds = !RectTransformUtility.RectangleContainsScreenPoint(textViewport, eventData.position, eventData.pressEventCamera);
            if (m_DragPositionOutOfBounds && m_DragCoroutine == null)
                m_DragCoroutine = StartCoroutine(MouseDragOutsideRect(eventData));
            UpdateKeyboardStringPosition();
            eventData.Use();
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        IEnumerator MouseDragOutsideRect(PointerEventData eventData)
        {
            while (m_UpdateDrag && m_DragPositionOutOfBounds)
            {
                Vector2 localMousePos;
                RectTransformUtility.ScreenPointToLocalPointInRectangle(textViewport, eventData.position, eventData.pressEventCamera, out localMousePos);
                Rect rect = textViewport.rect;
                if (multiLine)
                {
                    if (localMousePos.y > rect.yMax)
                        MoveUp(true, true);
                    else if (localMousePos.y < rect.yMin)
                        MoveDown(true, true);
                }
                else
                {
                    if (localMousePos.x < rect.xMin)
                        MoveLeft(true, false);
                    else if (localMousePos.x > rect.xMax)
                        MoveRight(true, false);
                }
                UpdateLabel();
                float delay = multiLine ? kVScrollSpeed : kHScrollSpeed;
                if (m_WaitForSecondsRealtime == null)
                    m_WaitForSecondsRealtime = new WaitForSecondsRealtime(delay);
                else
                    m_WaitForSecondsRealtime.waitTime = delay;
                yield return m_WaitForSecondsRealtime;
            }
            m_DragCoroutine = null;
        }
        public virtual void OnEndDrag(PointerEventData eventData)
        {
            if (!MayDrag(eventData))
                return;
            m_UpdateDrag = false;
        }
        public override void OnPointerDown(PointerEventData eventData)
        {
            if (!MayDrag(eventData))
                return;
            EventSystem.current.SetSelectedGameObject(gameObject, eventData);
            bool hadFocusBefore = m_AllowInput;
            base.OnPointerDown(eventData);
            if (InPlaceEditing() == false)
            {
                if (m_SoftKeyboard == null || !m_SoftKeyboard.active)
                {
                    OnSelect(eventData);
                    return;
                }
            }
            #if ENABLE_INPUT_SYSTEM
            Event.PopEvent(m_ProcessingEvent);
            bool shift = m_ProcessingEvent != null && (m_ProcessingEvent.modifiers & EventModifiers.Shift) != 0;
            #else
            bool shift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
            #endif
            // Check for Double Click
            bool isDoubleClick = false;
            float timeStamp = Time.unscaledTime;
            if (m_PointerDownClickStartTime + m_DoubleClickDelay > timeStamp)
                isDoubleClick = true;
            m_PointerDownClickStartTime = timeStamp;
            // Only set caret position if we didn't just get focus now.
            // Otherwise it will overwrite the select all on focus.
            if (hadFocusBefore || !m_OnFocusSelectAll)
            {
                CaretPosition insertionSide;
                int insertionIndex = TMP_TextUtilities.GetCursorIndexFromPosition(m_TextComponent, eventData.position, eventData.pressEventCamera, out insertionSide);
                if (shift)
                {
                    if (m_isRichTextEditingAllowed)
                    {
                        if (insertionSide == CaretPosition.Left)
                        {
                            stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
                        }
                        else if (insertionSide == CaretPosition.Right)
                        {
                            stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                        }
                    }
                    else
                    {
                        if (insertionSide == CaretPosition.Left)
                        {
                            stringSelectPositionInternal = insertionIndex == 0
                                ? m_TextComponent.textInfo.characterInfo[0].index
                                : m_TextComponent.textInfo.characterInfo[insertionIndex - 1].index + m_TextComponent.textInfo.characterInfo[insertionIndex - 1].stringLength;
                        }
                        else if (insertionSide == CaretPosition.Right)
                        {
                            stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                        }
                    }
                }
                else
                {
                    if (m_isRichTextEditingAllowed)
                    {
                        if (insertionSide == CaretPosition.Left)
                        {
                            stringPositionInternal = stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
                        }
                        else if (insertionSide == CaretPosition.Right)
                        {
                            stringPositionInternal = stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                        }
                    }
                    else
                    {
                        if (insertionSide == CaretPosition.Left)
                        {
                            stringPositionInternal = stringSelectPositionInternal = insertionIndex == 0
                                ? m_TextComponent.textInfo.characterInfo[0].index
                                : m_TextComponent.textInfo.characterInfo[insertionIndex - 1].index + m_TextComponent.textInfo.characterInfo[insertionIndex - 1].stringLength;
                        }
                        else if (insertionSide == CaretPosition.Right)
                        {
                            stringPositionInternal = stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                        }
                    }
                }
                if (isDoubleClick)
                {
                    int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextComponent, eventData.position, eventData.pressEventCamera);
                    if (wordIndex != -1)
                    {
                        // TODO: Should behavior be different if rich text editing is enabled or not?
                        // Select current word
                        caretPositionInternal = m_TextComponent.textInfo.wordInfo[wordIndex].firstCharacterIndex;
                        caretSelectPositionInternal = m_TextComponent.textInfo.wordInfo[wordIndex].lastCharacterIndex + 1;
                        stringPositionInternal = m_TextComponent.textInfo.characterInfo[caretPositionInternal].index;
                        stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].stringLength;
                    }
                    else
                    {
                        // Select current character
                        caretPositionInternal = insertionIndex;
                        caretSelectPositionInternal = caretPositionInternal + 1;
                        stringPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
                        stringSelectPositionInternal = stringPositionInternal + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
                    }
                }
                else
                {
                    caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
                }
                m_isSelectAll = false;
            }
            UpdateLabel();
            UpdateKeyboardStringPosition();
            eventData.Use();
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        protected enum EditState
        {
            Continue,
            Finish
        }
        protected EditState KeyPressed(Event evt)
        {
            var currentEventModifiers = evt.modifiers;
            bool ctrl = m_IsApplePlatform ? (currentEventModifiers & EventModifiers.Command) != 0 : (currentEventModifiers & EventModifiers.Control) != 0;
            bool shift = (currentEventModifiers & EventModifiers.Shift) != 0;
            bool alt = (currentEventModifiers & EventModifiers.Alt) != 0;
            bool ctrlOnly = ctrl && !alt && !shift;
            m_LastKeyCode = evt.keyCode;
            switch (evt.keyCode)
            {
                case KeyCode.Backspace:
                    {
                        Backspace();
                        return EditState.Continue;
                    }
                case KeyCode.Delete:
                    {
                        DeleteKey();
                        return EditState.Continue;
                    }
                case KeyCode.Home:
                    {
                        MoveToStartOfLine(shift, ctrl);
                        return EditState.Continue;
                    }
                case KeyCode.End:
                    {
                        MoveToEndOfLine(shift, ctrl);
                        return EditState.Continue;
                    }
                // Select All
                case KeyCode.A:
                    {
                        if (ctrlOnly)
                        {
                            SelectAll();
                            return EditState.Continue;
                        }
                        break;
                    }
                // Copy
                case KeyCode.C:
                    {
                        if (ctrlOnly)
                        {
                            if (inputType != InputType.Password)
                                clipboard = GetSelectedString();
                            else
                                clipboard = "";
                            return EditState.Continue;
                        }
                        break;
                    }
                // Paste
                case KeyCode.V:
                    {
                        if (ctrlOnly)
                        {
                            Append(clipboard);
                            return EditState.Continue;
                        }
                        break;
                    }
                // Cut
                case KeyCode.X:
                    {
                        if (ctrlOnly)
                        {
                            if (inputType != InputType.Password)
                                clipboard = GetSelectedString();
                            else
                                clipboard = "";
                            Delete();
                            UpdateTouchKeyboardFromEditChanges();
                            SendOnValueChangedAndUpdateLabel();
                            return EditState.Continue;
                        }
                        break;
                    }
                case KeyCode.LeftArrow:
                    {
                        MoveLeft(shift, ctrl);
                        return EditState.Continue;
                    }
                case KeyCode.RightArrow:
                    {
                        MoveRight(shift, ctrl);
                        return EditState.Continue;
                    }
                case KeyCode.UpArrow:
                    {
                        MoveUp(shift);
                        return EditState.Continue;
                    }
                case KeyCode.DownArrow:
                    {
                        MoveDown(shift);
                        return EditState.Continue;
                    }
                case KeyCode.PageUp:
                    {
                        MovePageUp(shift);
                        return EditState.Continue;
                    }
                case KeyCode.PageDown:
                    {
                        MovePageDown(shift);
                        return EditState.Continue;
                    }
                // Submit
                case KeyCode.Return:
                case KeyCode.KeypadEnter:
                    {
                        if (lineType != LineType.MultiLineNewline)
                        {
                            m_ReleaseSelection = true;
                            return EditState.Finish;
                        }
                        else
                        {
                            TMP_TextInfo textInfo = m_TextComponent.textInfo;
                            if (m_LineLimit > 0 && textInfo != null && textInfo.lineCount >= m_LineLimit)
                            {
                                m_ReleaseSelection = true;
                                return EditState.Finish;
                            }
                        }
                        break;
                    }
                case KeyCode.Escape:
                    {
                        m_ReleaseSelection = true;
                        m_WasCanceled = true;
                        return EditState.Finish;
                    }
            }
            char c = evt.character;
            // Don't allow return chars or tabulator key to be entered into single line fields.
            if (!multiLine && (c == '\t' || c == '\r' || c == '\n'))
                return EditState.Continue;
            // Convert carriage return and end-of-text characters to newline.
            if (c == '\r' || c == 3)
                c = '\n';
            // Convert Shift Enter to Vertical tab
            if (shift && c == '\n')
                c = '\v';
            if (IsValidChar(c))
            {
                Append(c);
            }
            if (c == 0)
            {
                if (compositionLength > 0)
                {
                    UpdateLabel();
                }
            }
            return EditState.Continue;
        }
        protected virtual bool IsValidChar(char c)
        {
            // Delete key on mac
            if (c == 127)
                return false;
            // Accept newline and tab
            if (c == '\t' || c == '\n')
                return true;
            // Control characters (not printable)
            if (c < 32)
                return false;
            return true;
            // With the addition of Dynamic support, I think this will best be handled by the text component.
            //return m_TextComponent.font.HasCharacter(c, true);
        }
        /// 
        /// Handle the specified event.
        /// 
        private Event m_ProcessingEvent = new Event();
        public void ProcessEvent(Event e)
        {
            KeyPressed(e);
        }
        /// 
        ///
        /// 
        /// 
        public virtual void OnUpdateSelected(BaseEventData eventData)
        {
            if (!isFocused)
                return;
            bool consumedEvent = false;
            EditState editState = EditState.Continue;
            while (Event.PopEvent(m_ProcessingEvent))
            {
                //Debug.Log("Event: " + m_ProcessingEvent.ToString() + "  IsCompositionActive= " + m_IsCompositionActive + "  Composition Length: " + compositionLength);
                EventType eventType = m_ProcessingEvent.rawType;
                if (eventType == EventType.KeyUp)
                    continue;
                if (eventType == EventType.KeyDown)
                {
                    consumedEvent = true;
                    // Special handling on OSX which produces more events which need to be suppressed.
                    if (m_IsCompositionActive && compositionLength == 0)
                    {
                        // Suppress other events related to navigation or termination of composition sequence.
                        if (m_ProcessingEvent.character == 0 && m_ProcessingEvent.modifiers == EventModifiers.None)
                            continue;
                    }
                    editState = KeyPressed(m_ProcessingEvent);
                    if (editState == EditState.Finish)
                    {
                        if (!m_WasCanceled)
                            SendOnSubmit();
                        DeactivateInputField();
                        break;
                    }
                    m_IsTextComponentUpdateRequired = true;
                    UpdateLabel();
                    continue;
                }
                switch (eventType)
                {
                    case EventType.ValidateCommand:
                    case EventType.ExecuteCommand:
                        switch (m_ProcessingEvent.commandName)
                        {
                            case "SelectAll":
                                SelectAll();
                                consumedEvent = true;
                                break;
                        }
                        break;
                }
            }
            // We must also consume events when IME is active to prevent them from being passed to the text field. // UUM-100552
            if (consumedEvent || (m_IsCompositionActive && compositionLength > 0))
            {
                UpdateLabel();
                eventData.Use();
            }
        }
        /// 
        ///
        /// 
        /// 
        public virtual void OnScroll(PointerEventData eventData)
        {
            // Return if Single Line
            if (m_LineType == LineType.SingleLine)
            {
                if (m_IScrollHandlerParent != null)
                    m_IScrollHandlerParent.OnScroll(eventData);
                return;
            }
            if (m_TextComponent.preferredHeight < m_TextViewport.rect.height)
                return;
            float scrollDirection = -eventData.scrollDelta.y;
            // Determine the current scroll position of the text within the viewport
            m_ScrollPosition = GetScrollPositionRelativeToViewport();
            m_ScrollPosition += (1f / m_TextComponent.textInfo.lineCount) * scrollDirection * m_ScrollSensitivity;
            m_ScrollPosition = Mathf.Clamp01(m_ScrollPosition);
            AdjustTextPositionRelativeToViewport(m_ScrollPosition);
            if (m_VerticalScrollbar)
            {
                m_VerticalScrollbar.value = m_ScrollPosition;
            }
            //Debug.Log(GetInstanceID() + "- Scroll Position:" + m_ScrollPosition);
        }
        float GetScrollPositionRelativeToViewport()
        {
            // Determine the current scroll position of the text within the viewport
            Rect viewportRect = m_TextViewport.rect;
            float scrollPosition = (m_TextComponent.textInfo.lineInfo[0].ascender + m_TextComponent.margin.y + m_TextComponent.margin.w - viewportRect.yMax + m_TextComponent.rectTransform.anchoredPosition.y) / ( m_TextComponent.preferredHeight - viewportRect.height);
            scrollPosition = (int)((scrollPosition * 1000) + 0.5f) / 1000.0f;
            return scrollPosition;
        }
        private string GetSelectedString()
        {
            if (!hasSelection)
                return "";
            int startPos = stringPositionInternal;
            int endPos = stringSelectPositionInternal;
            // Ensure pos is always less then selPos to make the code simpler
            if (startPos > endPos)
            {
                int temp = startPos;
                startPos = endPos;
                endPos = temp;
            }
            //for (int i = m_CaretPosition; i < m_CaretSelectPosition; i++)
            //{
            //    Debug.Log("Character [" + m_TextComponent.textInfo.characterInfo[i].character + "] using Style [" + m_TextComponent.textInfo.characterInfo[i].style + "] has been selected.");
            //}
            return text.Substring(startPos, endPos - startPos);
        }
        private int FindNextWordBegin()
        {
            if (stringSelectPositionInternal + 1 >= text.Length)
                return text.Length;
            int spaceLoc = text.IndexOfAny(kSeparators, stringSelectPositionInternal + 1);
            if (spaceLoc == -1)
                spaceLoc = text.Length;
            else
                spaceLoc++;
            return spaceLoc;
        }
        private void MoveRight(bool shift, bool ctrl)
        {
            if (hasSelection && !shift)
            {
                // By convention, if we have a selection and move right without holding shift,
                // we just place the cursor at the end.
                stringPositionInternal = stringSelectPositionInternal = Mathf.Max(stringPositionInternal, stringSelectPositionInternal);
                caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
                #if TMP_DEBUG_MODE
                    Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
                #endif
                return;
            }
            int position;
            if (ctrl)
                position = FindNextWordBegin();
            else
            {
                if (m_isRichTextEditingAllowed)
                {
                    // Special handling for Surrogate pairs and Diacritical marks.
                    if (stringSelectPositionInternal < text.Length && char.IsHighSurrogate(text[stringSelectPositionInternal]))
                        position = stringSelectPositionInternal + 2;
                    else
                        position = stringSelectPositionInternal + 1;
                }
                else
                {
                    // Special handling for 
                    if (m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].character == '\r' && m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal + 1].character == '\n')
                        position = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal + 1].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal + 1].stringLength;
                    else
                        position = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].stringLength;
                }
            }
            if (shift)
            {
                stringSelectPositionInternal = position;
                caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            }
            else
            {
                stringSelectPositionInternal = stringPositionInternal = position;
                // Only increase caret position as we cross character boundary.
                if (stringPositionInternal >= m_TextComponent.textInfo.characterInfo[caretPositionInternal].index + m_TextComponent.textInfo.characterInfo[caretPositionInternal].stringLength)
                    caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + "  Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + "  String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private int FindPrevWordBegin()
        {
            if (stringSelectPositionInternal - 2 < 0)
                return 0;
            int spaceLoc = text.LastIndexOfAny(kSeparators, stringSelectPositionInternal - 2);
            if (spaceLoc == -1)
                spaceLoc = 0;
            else
                spaceLoc++;
            return spaceLoc;
        }
        private void MoveLeft(bool shift, bool ctrl)
        {
            if (hasSelection && !shift)
            {
                // By convention, if we have a selection and move left without holding shift,
                // we just place the cursor at the start.
                stringPositionInternal = stringSelectPositionInternal = Mathf.Min(stringPositionInternal, stringSelectPositionInternal);
                caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
                #if TMP_DEBUG_MODE
                    Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
                #endif
                return;
            }
            int position;
            if (ctrl)
                position = FindPrevWordBegin();
            else
            {
                if (m_isRichTextEditingAllowed)
                {
                    // Special handling for Surrogate pairs and Diacritical marks.
                    if (stringSelectPositionInternal > 0 && char.IsLowSurrogate(text[stringSelectPositionInternal - 1]))
                        position = stringSelectPositionInternal - 2;
                    else
                        position =  stringSelectPositionInternal - 1;
                }
                else
                {
                    position = caretSelectPositionInternal < 1
                        ? m_TextComponent.textInfo.characterInfo[0].index
                        : m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].index;
                    // Special handling for 
                    if (position > 0 && m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].character == '\n' && m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 2].character == '\r')
                        position = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 2].index;
                }
            }
            if (shift)
            {
                stringSelectPositionInternal = position;
                caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            }
            else
            {
                stringSelectPositionInternal = stringPositionInternal = position;
                // Only decrease caret position as we cross character boundary.
                if (caretPositionInternal > 0 && stringPositionInternal <= m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index)
                    caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + "  Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + "  String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private int LineUpCharacterPosition(int originalPos, bool goToFirstChar)
        {
            if (originalPos >= m_TextComponent.textInfo.characterCount)
                originalPos -= 1;
            TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
            int originLine = originChar.lineNumber;
            // We are on the first line return first character
            if (originLine - 1 < 0)
                return goToFirstChar ? 0 : originalPos;
            int endCharIdx = m_TextComponent.textInfo.lineInfo[originLine].firstCharacterIndex - 1;
            int closest = -1;
            float distance = TMP_Math.FLOAT_MAX;
            float range = 0;
            for (int i = m_TextComponent.textInfo.lineInfo[originLine - 1].firstCharacterIndex; i < endCharIdx; ++i)
            {
                TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
                float d = originChar.origin - currentChar.origin;
                float r = d / (currentChar.xAdvance - currentChar.origin);
                if (r >= 0 && r <= 1)
                {
                    if (r < 0.5f)
                        return i;
                    else
                        return i + 1;
                }
                d = Mathf.Abs(d);
                if (d < distance)
                {
                    closest = i;
                    distance = d;
                    range = r;
                }
            }
            if (closest == -1) return endCharIdx;
            //Debug.Log("Returning nearest character with Range = " + range);
            if (range < 0.5f)
                return closest;
            else
                return closest + 1;
        }
        private int LineDownCharacterPosition(int originalPos, bool goToLastChar)
        {
            if (originalPos >= m_TextComponent.textInfo.characterCount)
                return m_TextComponent.textInfo.characterCount - 1; // text.Length;
            TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
            int originLine = originChar.lineNumber;
            //// We are on the last line return last character
            if (originLine + 1 >= m_TextComponent.textInfo.lineCount)
                return goToLastChar ? m_TextComponent.textInfo.characterCount - 1 : originalPos;
            // Need to determine end line for next line.
            int endCharIdx = m_TextComponent.textInfo.lineInfo[originLine + 1].lastCharacterIndex;
            int closest = -1;
            float distance = TMP_Math.FLOAT_MAX;
            float range = 0;
            for (int i = m_TextComponent.textInfo.lineInfo[originLine + 1].firstCharacterIndex; i < endCharIdx; ++i)
            {
                TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
                float d = originChar.origin - currentChar.origin;
                float r = d / (currentChar.xAdvance - currentChar.origin);
                if (r >= 0 && r <= 1)
                {
                    if (r < 0.5f)
                        return i;
                    else
                        return i + 1;
                }
                d = Mathf.Abs(d);
                if (d < distance)
                {
                    closest = i;
                    distance = d;
                    range = r;
                }
            }
            if (closest == -1) return endCharIdx;
            //Debug.Log("Returning nearest character with Range = " + range);
            if (range < 0.5f)
                return closest;
            else
                return closest + 1;
        }
         private int PageUpCharacterPosition(int originalPos, bool goToFirstChar)
        {
            if (originalPos >= m_TextComponent.textInfo.characterCount)
                originalPos -= 1;
            TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
            int originLine = originChar.lineNumber;
            // We are on the first line return first character
            if (originLine - 1 < 0)
                return goToFirstChar ? 0 : originalPos;
            float viewportHeight = m_TextViewport.rect.height;
            int newLine = originLine - 1;
            // Iterate through each subsequent line to find the first baseline that is not visible in the viewport.
            for (; newLine > 0; newLine--)
            {
                if (m_TextComponent.textInfo.lineInfo[newLine].baseline > m_TextComponent.textInfo.lineInfo[originLine].baseline + viewportHeight)
                    break;
            }
            int endCharIdx = m_TextComponent.textInfo.lineInfo[newLine].lastCharacterIndex;
            int closest = -1;
            float distance = TMP_Math.FLOAT_MAX;
            float range = 0;
            for (int i = m_TextComponent.textInfo.lineInfo[newLine].firstCharacterIndex; i < endCharIdx; ++i)
            {
                TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
                float d = originChar.origin - currentChar.origin;
                float r = d / (currentChar.xAdvance - currentChar.origin);
                if (r >= 0 && r <= 1)
                {
                    if (r < 0.5f)
                        return i;
                    else
                        return i + 1;
                }
                d = Mathf.Abs(d);
                if (d < distance)
                {
                    closest = i;
                    distance = d;
                    range = r;
                }
            }
            if (closest == -1) return endCharIdx;
            //Debug.Log("Returning nearest character with Range = " + range);
            if (range < 0.5f)
                return closest;
            else
                return closest + 1;
        }
         private int PageDownCharacterPosition(int originalPos, bool goToLastChar)
        {
            if (originalPos >= m_TextComponent.textInfo.characterCount)
                return m_TextComponent.textInfo.characterCount - 1;
            TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
            int originLine = originChar.lineNumber;
            // We are on the last line return last character
            if (originLine + 1 >= m_TextComponent.textInfo.lineCount)
                return goToLastChar ? m_TextComponent.textInfo.characterCount - 1 : originalPos;
            float viewportHeight = m_TextViewport.rect.height;
            int newLine = originLine + 1;
            // Iterate through each subsequent line to find the first baseline that is not visible in the viewport.
            for (; newLine < m_TextComponent.textInfo.lineCount - 1; newLine++)
            {
                if (m_TextComponent.textInfo.lineInfo[newLine].baseline < m_TextComponent.textInfo.lineInfo[originLine].baseline - viewportHeight)
                    break;
            }
            // Need to determine end line for next line.
            int endCharIdx = m_TextComponent.textInfo.lineInfo[newLine].lastCharacterIndex;
            int closest = -1;
            float distance = TMP_Math.FLOAT_MAX;
            float range = 0;
            for (int i = m_TextComponent.textInfo.lineInfo[newLine].firstCharacterIndex; i < endCharIdx; ++i)
            {
                TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
                float d = originChar.origin - currentChar.origin;
                float r = d / (currentChar.xAdvance - currentChar.origin);
                if (r >= 0 && r <= 1)
                {
                    if (r < 0.5f)
                        return i;
                    else
                        return i + 1;
                }
                d = Mathf.Abs(d);
                if (d < distance)
                {
                    closest = i;
                    distance = d;
                    range = r;
                }
            }
            if (closest == -1) return endCharIdx;
            if (range < 0.5f)
                return closest;
            else
                return closest + 1;
        }
        private void MoveDown(bool shift)
        {
            MoveDown(shift, true);
        }
        private void MoveDown(bool shift, bool goToLastChar)
        {
            if (hasSelection && !shift)
            {
                // If we have a selection and press down without shift,
                // set caret to end of selection before we move it down.
                caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
            }
            int position = multiLine ? LineDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : m_TextComponent.textInfo.characterCount - 1; // text.Length;
            if (shift)
            {
                caretSelectPositionInternal = position;
                stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            else
            {
                caretSelectPositionInternal = caretPositionInternal = position;
                stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private void MoveUp(bool shift)
        {
            MoveUp(shift, true);
        }
        private void MoveUp(bool shift, bool goToFirstChar)
        {
            if (hasSelection && !shift)
            {
                // If we have a selection and press up without shift,
                // set caret position to start of selection before we move it up.
                caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
            }
            int position = multiLine ? LineUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
            if (shift)
            {
                caretSelectPositionInternal = position;
                stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            else
            {
                caretSelectPositionInternal = caretPositionInternal = position;
                stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private void MovePageUp(bool shift)
        {
            MovePageUp(shift, true);
        }
        private void MovePageUp(bool shift, bool goToFirstChar)
        {
            if (hasSelection && !shift)
            {
                // If we have a selection and press up without shift,
                // set caret position to start of selection before we move it up.
                caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
            }
            int position = multiLine ? PageUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
            if (shift)
            {
                caretSelectPositionInternal = position;
                stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            else
            {
                caretSelectPositionInternal = caretPositionInternal = position;
                stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            // Scroll to top of viewport
            //int currentLine = m_TextComponent.textInfo.characterInfo[position].lineNumber;
            //float lineAscender = m_TextComponent.textInfo.lineInfo[currentLine].ascender;
            // Adjust text area up or down if not in single line mode.
            if (m_LineType != LineType.SingleLine)
            {
                float offset = m_TextViewport.rect.height; // m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + lineAscender);
                float topTextBounds = m_TextComponent.rectTransform.position.y + m_TextComponent.textBounds.max.y;
                float topViewportBounds = m_TextViewport.position.y + m_TextViewport.rect.yMax;
                offset = topViewportBounds > topTextBounds + offset ? offset : topViewportBounds - topTextBounds;
                m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, offset);
                AssignPositioningIfNeeded();
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private void MovePageDown(bool shift)
        {
            MovePageDown(shift, true);
        }
        private void MovePageDown(bool shift, bool goToLastChar)
        {
             if (hasSelection && !shift)
            {
                // If we have a selection and press down without shift,
                // set caret to end of selection before we move it down.
                caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
            }
            int position = multiLine ? PageDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : m_TextComponent.textInfo.characterCount - 1;
            if (shift)
            {
                caretSelectPositionInternal = position;
                stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            else
            {
                caretSelectPositionInternal = caretPositionInternal = position;
                stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
            }
            // Scroll to top of viewport
            //int currentLine = m_TextComponent.textInfo.characterInfo[position].lineNumber;
            //float lineAscender = m_TextComponent.textInfo.lineInfo[currentLine].ascender;
            // Adjust text area up or down if not in single line mode.
            if (m_LineType != LineType.SingleLine)
            {
                float offset = m_TextViewport.rect.height; // m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + lineAscender);
                float bottomTextBounds = m_TextComponent.rectTransform.position.y + m_TextComponent.textBounds.min.y;
                float bottomViewportBounds = m_TextViewport.position.y + m_TextViewport.rect.yMin;
                offset = bottomViewportBounds > bottomTextBounds + offset ? offset : bottomViewportBounds - bottomTextBounds;
                m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, offset);
                AssignPositioningIfNeeded();
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private void Delete()
        {
            if (m_ReadOnly)
                return;
            if (m_StringPosition == m_StringSelectPosition)
                return;
            if (m_isRichTextEditingAllowed || m_isSelectAll)
            {
                // Handling of Delete when Rich Text is allowed.
                if (m_StringPosition < m_StringSelectPosition)
                {
                    m_Text = text.Remove(m_StringPosition, m_StringSelectPosition - m_StringPosition);
                    m_StringSelectPosition = m_StringPosition;
                }
                else
                {
                    m_Text = text.Remove(m_StringSelectPosition, m_StringPosition - m_StringSelectPosition);
                    m_StringPosition = m_StringSelectPosition;
                }
                if (m_isSelectAll)
                {
                    m_CaretPosition = m_CaretSelectPosition = 0;
                    m_isSelectAll = false;
                }
            }
            else
            {
                if (m_CaretPosition < m_CaretSelectPosition)
                {
                    int index = ClampArrayIndex(m_CaretSelectPosition - 1);
                    m_StringPosition = m_TextComponent.textInfo.characterInfo[m_CaretPosition].index;
                    m_StringSelectPosition = m_TextComponent.textInfo.characterInfo[index].index + m_TextComponent.textInfo.characterInfo[index].stringLength;
                    m_Text = text.Remove(m_StringPosition, m_StringSelectPosition - m_StringPosition);
                    m_StringSelectPosition = m_StringPosition;
                    m_CaretSelectPosition = m_CaretPosition;
                }
                else
                {
                    int index = ClampArrayIndex(m_CaretPosition - 1);
                    m_StringPosition = m_TextComponent.textInfo.characterInfo[index].index + m_TextComponent.textInfo.characterInfo[index].stringLength;
                    m_StringSelectPosition = m_TextComponent.textInfo.characterInfo[m_CaretSelectPosition].index;
                    m_Text = text.Remove(m_StringSelectPosition, m_StringPosition - m_StringSelectPosition);
                    m_StringPosition = m_StringSelectPosition;
                    m_CaretPosition = m_CaretSelectPosition;
                }
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        /// 
        /// Handling of DEL key
        /// 
        private void DeleteKey()
        {
            if (m_ReadOnly)
                return;
            if (hasSelection)
            {
                m_HasTextBeenRemoved = true;
                Delete();
                UpdateTouchKeyboardFromEditChanges();
                SendOnValueChangedAndUpdateLabel();
            }
            else
            {
                if (m_isRichTextEditingAllowed)
                {
                    if (stringPositionInternal < text.Length)
                    {
                        // Special handling for Surrogate Pairs
                        if (char.IsHighSurrogate(text[stringPositionInternal]))
                            m_Text = text.Remove(stringPositionInternal, 2);
                        else
                            m_Text = text.Remove(stringPositionInternal, 1);
                        m_HasTextBeenRemoved = true;
                        UpdateTouchKeyboardFromEditChanges();
                        SendOnValueChangedAndUpdateLabel();
                    }
                }
                else
                {
                    if (caretPositionInternal < m_TextComponent.textInfo.characterCount - 1)
                    {
                        int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionInternal].stringLength;
                        // Special handling for 
                        if (m_TextComponent.textInfo.characterInfo[caretPositionInternal].character == '\r' && m_TextComponent.textInfo.characterInfo[caretPositionInternal + 1].character == '\n')
                            numberOfCharactersToRemove += m_TextComponent.textInfo.characterInfo[caretPositionInternal + 1].stringLength;
                        // Adjust string position to skip any potential rich text tags.
                        int nextCharacterStringPosition = m_TextComponent.textInfo.characterInfo[caretPositionInternal].index;
                        m_Text = text.Remove(nextCharacterStringPosition, numberOfCharactersToRemove);
                        m_HasTextBeenRemoved = true;
                        SendOnValueChangedAndUpdateLabel();
                    }
                }
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        /// 
        /// Handling of Backspace key
        /// 
        private void Backspace()
        {
            if (m_ReadOnly)
                return;
            if (hasSelection)
            {
                m_HasTextBeenRemoved = true;
                Delete();
                UpdateTouchKeyboardFromEditChanges();
                SendOnValueChangedAndUpdateLabel();
            }
            else
            {
                if (m_isRichTextEditingAllowed)
                {
                    if (stringPositionInternal > 0)
                    {
                        int numberOfCharactersToRemove = 1;
                        // Special handling for Surrogate pairs and Diacritical marks
                        if (char.IsLowSurrogate(text[stringPositionInternal - 1]))
                            numberOfCharactersToRemove = 2;
                        stringSelectPositionInternal = stringPositionInternal = stringPositionInternal - numberOfCharactersToRemove;
                        m_Text = text.Remove(stringPositionInternal, numberOfCharactersToRemove);
                        caretSelectPositionInternal = caretPositionInternal = caretPositionInternal - 1;
                        m_HasTextBeenRemoved = true;
                        UpdateTouchKeyboardFromEditChanges();
                        SendOnValueChangedAndUpdateLabel();
                    }
                }
                else
                {
                    if (caretPositionInternal > 0)
                    {
                        int caretPositionIndex = caretPositionInternal - 1;
                        int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionIndex].stringLength;
                        // Special handling for 
                        if (caretPositionIndex > 0 && m_TextComponent.textInfo.characterInfo[caretPositionIndex].character == '\n' && m_TextComponent.textInfo.characterInfo[caretPositionIndex - 1].character == '\r')
                        {
                            numberOfCharactersToRemove += m_TextComponent.textInfo.characterInfo[caretPositionIndex - 1].stringLength;
                            caretPositionIndex -= 1;
                        }
                        // Delete the previous character
                        m_Text = text.Remove(m_TextComponent.textInfo.characterInfo[caretPositionIndex].index, numberOfCharactersToRemove);
                        // Get new adjusted string position
                        stringSelectPositionInternal = stringPositionInternal = caretPositionInternal < 1
                            ? m_TextComponent.textInfo.characterInfo[0].index
                            : m_TextComponent.textInfo.characterInfo[caretPositionIndex].index;
                        caretSelectPositionInternal = caretPositionInternal = caretPositionIndex;
                    }
                    m_HasTextBeenRemoved = true;
                    UpdateTouchKeyboardFromEditChanges();
                    SendOnValueChangedAndUpdateLabel();
                }
            }
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        /// 
        /// Append the specified text to the end of the current.
        /// 
        protected virtual void Append(string input)
        {
            if (m_ReadOnly)
                return;
            if (InPlaceEditing() == false)
                return;
            for (int i = 0, imax = input.Length; i < imax; ++i)
            {
                char c = input[i];
                if (c >= ' ' || c == '\t' || c == '\r' || c == '\n')
                {
                    Append(c);
                }
            }
        }
        protected virtual void Append(char input)
        {
            if (m_ReadOnly)
                return;
            if (InPlaceEditing() == false)
                return;
            // If we have an input validator, validate the input first
            int insertionPosition = Mathf.Min(stringPositionInternal, stringSelectPositionInternal);
            //Get the text based on selection for validation instead of whole text(case 1253193).
            var validateText = text;
            if (selectionFocusPosition != selectionAnchorPosition)
            {
                m_HasTextBeenRemoved = true;
                if (m_isRichTextEditingAllowed || m_isSelectAll)
                {
                    // Handling of Delete when Rich Text is allowed.
                    if (m_StringPosition < m_StringSelectPosition)
                        validateText = text.Remove(m_StringPosition, m_StringSelectPosition - m_StringPosition);
                    else
                        validateText = text.Remove(m_StringSelectPosition, m_StringPosition - m_StringSelectPosition);
                }
                else
                {
                    if (m_CaretPosition < m_CaretSelectPosition)
                    {
                        m_StringPosition = m_TextComponent.textInfo.characterInfo[m_CaretPosition].index;
                        m_StringSelectPosition = m_TextComponent.textInfo.characterInfo[m_CaretSelectPosition - 1].index + m_TextComponent.textInfo.characterInfo[m_CaretSelectPosition - 1].stringLength;
                        validateText = text.Remove(m_StringPosition, m_StringSelectPosition - m_StringPosition);
                    }
                    else
                    {
                        m_StringPosition = m_TextComponent.textInfo.characterInfo[m_CaretPosition - 1].index + m_TextComponent.textInfo.characterInfo[m_CaretPosition - 1].stringLength;
                        m_StringSelectPosition = m_TextComponent.textInfo.characterInfo[m_CaretSelectPosition].index;
                        validateText = text.Remove(m_StringSelectPosition, m_StringPosition - m_StringSelectPosition);
                    }
                }
            }
            if (onValidateInput != null)
            {
                input = onValidateInput(validateText, insertionPosition, input);
            }
            else if (characterValidation == CharacterValidation.CustomValidator)
            {
                input = Validate(validateText, insertionPosition, input);
                if (input == 0) return;
                if (!char.IsHighSurrogate(input))
                    m_CaretSelectPosition = m_CaretPosition += 1;
                SendOnValueChanged();
                UpdateLabel();
                return;
            }
            else if (characterValidation != CharacterValidation.None)
            {
                input = Validate(validateText, insertionPosition, input);
            }
            // If the input is invalid, skip it
            if (input == 0)
                return;
            // Append the character and update the label
            Insert(input);
        }
        // Insert the character and update the label.
        private void Insert(char c)
        {
            if (m_ReadOnly)
                return;
            //Debug.Log("Inserting character " + m_IsCompositionActive);
            string replaceString = c.ToString();
            Delete();
            // Can't go past the character limit
            if (characterLimit > 0 && text.Length >= characterLimit)
                return;
            m_Text = text.Insert(m_StringPosition, replaceString);
            if (!char.IsHighSurrogate(c))
                m_CaretSelectPosition = m_CaretPosition += 1;
            m_StringSelectPosition = m_StringPosition += 1;
            UpdateTouchKeyboardFromEditChanges();
            SendOnValueChanged();
            #if TMP_DEBUG_MODE
                Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + "  String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
            #endif
        }
        private void UpdateTouchKeyboardFromEditChanges()
        {
            // Update the TouchKeyboard's text from edit changes
            // if in-place editing is allowed
            if (m_SoftKeyboard != null && InPlaceEditing())
            {
                m_SoftKeyboard.text = m_Text;
            }
        }
        private void SendOnValueChangedAndUpdateLabel()
        {
            UpdateLabel();
            SendOnValueChanged();
        }
        private void SendOnValueChanged()
        {
            if (onValueChanged != null)
                onValueChanged.Invoke(text);
        }
        /// 
        /// Submit the input field's text.
        /// 
        protected void SendOnEndEdit()
        {
            if (onEndEdit != null)
                onEndEdit.Invoke(m_Text);
        }
        protected void SendOnSubmit()
        {
            if (onSubmit != null)
                onSubmit.Invoke(m_Text);
        }
        protected void SendOnFocus()
        {
            if (onSelect != null)
                onSelect.Invoke(m_Text);
        }
        protected void SendOnFocusLost()
        {
            if (onDeselect != null)
                onDeselect.Invoke(m_Text);
        }
        protected void SendOnTextSelection()
        {
            m_isSelected = true;
            if (onTextSelection != null)
                onTextSelection.Invoke(m_Text, stringPositionInternal, stringSelectPositionInternal);
        }
        protected void SendOnEndTextSelection()
        {
            if (!m_isSelected) return;
            if (onEndTextSelection != null)
                onEndTextSelection.Invoke(m_Text, stringPositionInternal, stringSelectPositionInternal);
            m_isSelected = false;
        }
        protected void SendTouchScreenKeyboardStatusChanged()
        {
            if (m_SoftKeyboard != null && onTouchScreenKeyboardStatusChanged != null)
                onTouchScreenKeyboardStatusChanged.Invoke(m_SoftKeyboard.status);
        }
        /// 
        /// Update the visual text Text.
        /// 
        protected void UpdateLabel()
        {
            if (m_TextComponent != null && m_TextComponent.font != null && m_PreventCallback == false)
            {
                // Prevent callback from the text component as we assign new text. This is to prevent a recursive call.
                m_PreventCallback = true;
                string fullText;
                if (compositionLength > 0 && m_ReadOnly == false)
                {
                    //Input.imeCompositionMode = IMECompositionMode.On;
                    // Handle selections
                    Delete();
                    if (m_RichText)
                        fullText = text.Substring(0, m_StringPosition) +  "" + compositionString + "" + text.Substring(m_StringPosition);
                    else
                        fullText = text.Substring(0, m_StringPosition) +  compositionString + text.Substring(m_StringPosition);
                    m_IsCompositionActive = true;
                    //Debug.Log("[" + Time.frameCount + "] Handling IME Input");
                }
                else
                {
                    fullText = text;
                    m_IsCompositionActive = false;
                    m_ShouldUpdateIMEWindowPosition = true;
                }
                //Debug.Log("Handling IME Input... [" + compositionString + "] of length [" + compositionLength + "] at StringPosition [" + m_StringPosition + "]  IsActive [" + m_IsCompositionActive + "]");
                string processed;
                if (inputType == InputType.Password)
                    processed = new string(asteriskChar, fullText.Length);
                else
                    processed = fullText;
                bool isEmpty = string.IsNullOrEmpty(fullText);
                if (m_Placeholder != null)
                    m_Placeholder.enabled = isEmpty;
                if (!isEmpty && m_ReadOnly == false)
                {
                    SetCaretVisible();
                }
                m_TextComponent.text = processed + "\u200B"; // Extra space is added for Caret tracking.
                // Rebuild layout if using Layout components.
                if (m_IsDrivenByLayoutComponents)
                    LayoutRebuilder.MarkLayoutForRebuild(m_RectTransform);
                // Special handling to limit the number of lines of text in the Input Field.
                if (m_LineLimit > 0)
                {
                    m_TextComponent.ForceMeshUpdate();
                    TMP_TextInfo textInfo = m_TextComponent.textInfo;
                    // Check if text exceeds maximum number of lines.
                    if (textInfo != null && textInfo.lineCount > m_LineLimit)
                    {
                        int lastValidCharacterIndex = textInfo.lineInfo[m_LineLimit - 1].lastCharacterIndex;
                        int characterStringIndex = textInfo.characterInfo[lastValidCharacterIndex].index + textInfo.characterInfo[lastValidCharacterIndex].stringLength;
                        text = processed.Remove(characterStringIndex, processed.Length - characterStringIndex);
                        m_TextComponent.text = text + "\u200B";
                    }
                }
                if (m_IsTextComponentUpdateRequired || m_VerticalScrollbar && !(m_IsCaretPositionDirty && m_IsStringPositionDirty))
                {
                    m_IsTextComponentUpdateRequired = false;
                    m_TextComponent.ForceMeshUpdate();
                }
                MarkGeometryAsDirty();
                m_PreventCallback = false;
            }
        }
        void UpdateScrollbar()
        {
            // Update Scrollbar
            if (m_VerticalScrollbar)
            {
                Rect viewportRect = m_TextViewport.rect;
                float size = viewportRect.height / m_TextComponent.preferredHeight;
                m_VerticalScrollbar.size = size;
                m_VerticalScrollbar.value = GetScrollPositionRelativeToViewport();
                //Debug.Log(GetInstanceID() + "- UpdateScrollbar() - Updating Scrollbar... Value: " + m_VerticalScrollbar.value);
            }
        }
        /// 
        /// Function to update the vertical position of the text container when OnValueChanged event is received from the Scrollbar.
        /// 
        /// 
        void OnScrollbarValueChange(float value)
        {
            //if (m_IsUpdatingScrollbarValues)
            //{
            //    m_IsUpdatingScrollbarValues = false;
            //    return;
            //}
            if (value < 0 || value > 1) return;
            AdjustTextPositionRelativeToViewport(value);
            m_ScrollPosition = value;
            //Debug.Log(GetInstanceID() + "- OnScrollbarValueChange() - Scrollbar value is: " + value + "  Transform POS: " + m_TextComponent.rectTransform.anchoredPosition);
        }
        void UpdateMaskRegions()
        {
            // TODO: Figure out a better way to handle adding an offset to the masking region
            // This region is defined by the RectTransform of the GameObject that contains the RectMask2D component.
            /*
            // Update Masking Region
            if (m_TextViewportRectMask != null)
            {
                Rect viewportRect = m_TextViewportRectMask.canvasRect;
                if (viewportRect != m_CachedViewportRect)
                {
                    m_CachedViewportRect = viewportRect;
                    viewportRect.min -= m_TextViewport.offsetMin * 0.5f;
                    viewportRect.max -= m_TextViewport.offsetMax * 0.5f;
                    if (m_CachedInputRenderer != null)
                        m_CachedInputRenderer.EnableRectClipping(viewportRect);
                    if (m_TextComponent.canvasRenderer != null)
                        m_TextComponent.canvasRenderer.EnableRectClipping(viewportRect);
                    if (m_Placeholder != null && m_Placeholder.enabled)
                        m_Placeholder.canvasRenderer.EnableRectClipping(viewportRect);
                }
            }
            */
        }
        /// 
        /// Adjusts the relative position of the body of the text relative to the viewport.
        /// 
        /// 
        void AdjustTextPositionRelativeToViewport (float relativePosition)
        {
            if (m_TextViewport == null)
                return;
            TMP_TextInfo textInfo = m_TextComponent.textInfo;
            // Check to make sure we have valid data and lines to query.
            if (textInfo == null || textInfo.lineInfo == null || textInfo.lineCount == 0 || textInfo.lineCount > textInfo.lineInfo.Length) return;
            float verticalAlignmentOffset = 0;
            float textHeight = m_TextComponent.preferredHeight;
            switch (m_TextComponent.verticalAlignment)
            {
                case VerticalAlignmentOptions.Top:
                    verticalAlignmentOffset = 0;
                    break;
                case VerticalAlignmentOptions.Middle:
                    verticalAlignmentOffset = 0.5f;
                    break;
                case VerticalAlignmentOptions.Bottom:
                    verticalAlignmentOffset = 1.0f;
                    break;
                case VerticalAlignmentOptions.Baseline:
                    break;
                case VerticalAlignmentOptions.Geometry:
                    verticalAlignmentOffset = 0.5f;
                    textHeight = m_TextComponent.bounds.size.y;
                    break;
                case VerticalAlignmentOptions.Capline:
                    verticalAlignmentOffset = 0.5f;
                    break;
            }
            m_TextComponent.rectTransform.anchoredPosition = new Vector2(m_TextComponent.rectTransform.anchoredPosition.x, (textHeight - m_TextViewport.rect.height) * (relativePosition - verticalAlignmentOffset));
            AssignPositioningIfNeeded();
            //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"));
        }
        private int GetCaretPositionFromStringIndex(int stringIndex)
        {
            int count = m_TextComponent.textInfo.characterCount;
            for (int i = 0; i < count; i++)
            {
                if (m_TextComponent.textInfo.characterInfo[i].index >= stringIndex)
                    return i;
            }
            return count;
        }
        /// 
        /// Returns / places the caret before the given character at the string index.
        /// 
        /// 
        /// 
        private int GetMinCaretPositionFromStringIndex(int stringIndex)
        {
            int count = m_TextComponent.textInfo.characterCount;
            for (int i = 0; i < count; i++)
            {
                if (stringIndex < m_TextComponent.textInfo.characterInfo[i].index + m_TextComponent.textInfo.characterInfo[i].stringLength)
                    return i;
            }
            return count;
        }
        /// 
        /// Returns / places the caret after the given character at the string index.
        /// 
        /// 
        /// 
        private int GetMaxCaretPositionFromStringIndex(int stringIndex)
        {
            int count = m_TextComponent.textInfo.characterCount;
            for (int i = 0; i < count; i++)
            {
                if (m_TextComponent.textInfo.characterInfo[i].index >= stringIndex)
                    return i;
            }
            return count;
        }
        private int GetStringIndexFromCaretPosition(int caretPosition)
        {
            // Clamp values between 0 and character count.
            ClampCaretPos(ref caretPosition);
            return m_TextComponent.textInfo.characterInfo[caretPosition].index;
        }
        void UpdateStringIndexFromCaretPosition()
        {
            stringPositionInternal = GetStringIndexFromCaretPosition(m_CaretPosition);
            stringSelectPositionInternal = GetStringIndexFromCaretPosition(m_CaretSelectPosition);
            m_IsStringPositionDirty = false;
        }
        void UpdateCaretPositionFromStringIndex()
        {
            caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
            caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            m_IsCaretPositionDirty = false;
        }
        public void ForceLabelUpdate()
        {
            UpdateLabel();
        }
        private void MarkGeometryAsDirty()
        {
            #if UNITY_EDITOR
            if (!Application.isPlaying || UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this))
                return;
            #endif
            CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
        }
        public virtual void Rebuild(CanvasUpdate update)
        {
            switch (update)
            {
                case CanvasUpdate.LatePreRender:
                    UpdateGeometry();
                    break;
            }
        }
        public virtual void LayoutComplete()
        { }
        public virtual void GraphicUpdateComplete()
        { }
        private void UpdateGeometry()
        {
            #if UNITY_EDITOR
            if (!Application.isPlaying)
                return;
            #endif
            // No need to draw a cursor on mobile as its handled by the devices keyboard with the exception of UWP.
            if (InPlaceEditing() == false && isUWP() == false)
                return;
            if (m_CachedInputRenderer == null)
                return;
            OnFillVBO(mesh);
            m_CachedInputRenderer.SetMesh(mesh);
        }
        /// 
        /// Method to keep the Caret RectTransform properties in sync with the text object's RectTransform
        /// 
        private void AssignPositioningIfNeeded()
        {
            if (m_TextComponent != null && caretRectTrans != null &&
                (caretRectTrans.localPosition != m_TextComponent.rectTransform.localPosition ||
                 caretRectTrans.localRotation != m_TextComponent.rectTransform.localRotation ||
                 caretRectTrans.localScale != m_TextComponent.rectTransform.localScale ||
                 caretRectTrans.anchorMin != m_TextComponent.rectTransform.anchorMin ||
                 caretRectTrans.anchorMax != m_TextComponent.rectTransform.anchorMax ||
                 caretRectTrans.anchoredPosition != m_TextComponent.rectTransform.anchoredPosition ||
                 caretRectTrans.sizeDelta != m_TextComponent.rectTransform.sizeDelta ||
                 caretRectTrans.pivot != m_TextComponent.rectTransform.pivot))
            {
                caretRectTrans.localPosition = m_TextComponent.rectTransform.localPosition;
                caretRectTrans.localRotation = m_TextComponent.rectTransform.localRotation;
                caretRectTrans.localScale = m_TextComponent.rectTransform.localScale;
                caretRectTrans.anchorMin = m_TextComponent.rectTransform.anchorMin;
                caretRectTrans.anchorMax = m_TextComponent.rectTransform.anchorMax;
                caretRectTrans.anchoredPosition = m_TextComponent.rectTransform.anchoredPosition;
                caretRectTrans.sizeDelta = m_TextComponent.rectTransform.sizeDelta;
                caretRectTrans.pivot = m_TextComponent.rectTransform.pivot;
            }
        }
        private void OnFillVBO(Mesh vbo)
        {
            using (var helper = new VertexHelper())
            {
                if (!isFocused && !m_SelectionStillActive)
                {
                    helper.FillMesh(vbo);
                    return;
                }
                if (m_IsStringPositionDirty)
                    UpdateStringIndexFromCaretPosition();
                if (m_IsCaretPositionDirty)
                    UpdateCaretPositionFromStringIndex();
                if (!hasSelection)
                {
                    GenerateCaret(helper, Vector2.zero);
                    SendOnEndTextSelection();
                }
                else
                {
                    GenerateHighlight(helper, Vector2.zero);
                    SendOnTextSelection();
                }
                helper.FillMesh(vbo);
            }
        }
        private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset)
        {
            if (m_CaretVisible == false || m_TextComponent.canvas == null || m_ReadOnly)
                return;
            if (m_CursorVerts == null)
            {
                CreateCursorVerts();
            }
            // TODO: Optimize to only update the caret position when needed.
            Vector2 startPosition = Vector2.zero;
            float height = 0;
            TMP_CharacterInfo currentCharacter;
            // Make sure caret position does not exceed characterInfo array size or less than zero.
            if (caretPositionInternal >= m_TextComponent.textInfo.characterInfo.Length || caretPositionInternal < 0)
                return;
            int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
            // Caret is positioned at the origin for the first character of each lines and at the advance for subsequent characters.
            if (caretPositionInternal == m_TextComponent.textInfo.lineInfo[currentLine].firstCharacterIndex)
            {
                currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal];
                height = currentCharacter.ascender - currentCharacter.descender;
                if (m_TextComponent.verticalAlignment == VerticalAlignmentOptions.Geometry)
                    startPosition = new Vector2(currentCharacter.origin, 0 - height / 2);
                else
                    startPosition = new Vector2(currentCharacter.origin, currentCharacter.descender);
            }
            else
            {
                currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1];
                height = currentCharacter.ascender - currentCharacter.descender;
                if (m_TextComponent.verticalAlignment == VerticalAlignmentOptions.Geometry)
                    startPosition = new Vector2(currentCharacter.xAdvance, 0 - height / 2);
                else
                    startPosition = new Vector2(currentCharacter.xAdvance, currentCharacter.descender);
            }
            if (m_SoftKeyboard != null && compositionLength == 0)
            {
                int selectionStart = m_StringPosition;
                int softKeyboardStringLength = m_SoftKeyboard.text == null ? 0 : m_SoftKeyboard.text.Length;
                if (selectionStart < 0)
                    selectionStart = 0;
                if (selectionStart > softKeyboardStringLength)
                    selectionStart = softKeyboardStringLength;
                m_SoftKeyboard.selection = new RangeInt(selectionStart, 0);
            }
            // Adjust the position of the RectTransform based on the caret position in the viewport (only if we have focus).
            if (isFocused && startPosition != m_LastPosition || m_forceRectTransformAdjustment || m_HasTextBeenRemoved)
                AdjustRectTransformRelativeToViewport(startPosition, height, currentCharacter.isVisible);
            m_LastPosition = startPosition;
            // Clamp Caret height
            float top = startPosition.y + height;
            float bottom = top - height;
            // Compute the width of the caret which is based on the line height of the primary font asset.
            //float width = m_CaretWidth;
            TMP_FontAsset fontAsset = m_TextComponent.font;
            float baseScale = (m_TextComponent.fontSize / fontAsset.m_FaceInfo.pointSize * fontAsset.m_FaceInfo.scale);
            float width = m_CaretWidth * fontAsset.faceInfo.lineHeight * baseScale * 0.05f;
            width = Mathf.Max(width, 1.0f);
            m_CursorVerts[0].position = new Vector3(startPosition.x, bottom, 0.0f);
            m_CursorVerts[1].position = new Vector3(startPosition.x, top, 0.0f);
            m_CursorVerts[2].position = new Vector3(startPosition.x + width, top, 0.0f);
            m_CursorVerts[3].position = new Vector3(startPosition.x + width, bottom, 0.0f);
            // Set Vertex Color for the caret color.
            m_CursorVerts[0].color = caretColor;
            m_CursorVerts[1].color = caretColor;
            m_CursorVerts[2].color = caretColor;
            m_CursorVerts[3].color = caretColor;
            vbo.AddUIVertexQuad(m_CursorVerts);
            // Update position of IME window when necessary.
            if (m_ShouldUpdateIMEWindowPosition || currentLine != m_PreviousIMEInsertionLine)
            {
                m_ShouldUpdateIMEWindowPosition = false;
                m_PreviousIMEInsertionLine = currentLine;
                // Calculate position of IME Window in screen space.
                Camera cameraRef;
                if (m_TextComponent.canvas.renderMode == RenderMode.ScreenSpaceOverlay)
                    cameraRef = null;
                else
                {
                    cameraRef = m_TextComponent.canvas.worldCamera;
                    if (cameraRef == null)
                        cameraRef = Camera.current;
                }
                Vector3 cursorPosition = m_CachedInputRenderer.gameObject.transform.TransformPoint(m_CursorVerts[0].position);
                Vector2 screenPosition = RectTransformUtility.WorldToScreenPoint(cameraRef, cursorPosition);
                screenPosition.y = Screen.height - screenPosition.y;
                if (inputSystem != null)
                    inputSystem.compositionCursorPos = screenPosition;
                //Debug.Log("[" + Time.frameCount + "] Updating IME Window position  Cursor Pos: (" + cursorPosition + ")  Screen Pos: (" + screenPosition + ") with Composition Length: " + compositionLength);
            }
            //#if TMP_DEBUG_MODE
            //Debug.Log("Caret position updated at frame: " + Time.frameCount);
            //#endif
        }
        private void CreateCursorVerts()
        {
            m_CursorVerts = new UIVertex[4];
            for (int i = 0; i < m_CursorVerts.Length; i++)
            {
                m_CursorVerts[i] = UIVertex.simpleVert;
                m_CursorVerts[i].uv0 = Vector2.zero;
            }
        }
        private void GenerateHighlight(VertexHelper vbo, Vector2 roundingOffset)
        {
            // Update Masking Region
            UpdateMaskRegions();
            // Make sure caret position does not exceed characterInfo array size.
            //if (caretSelectPositionInternal >= m_TextComponent.textInfo.characterInfo.Length)
            //    return;
            TMP_TextInfo textInfo = m_TextComponent.textInfo;
            // Return if character count is zero as there is nothing to highlight.
            if (textInfo.characterCount == 0)
                return;
            m_CaretPosition = GetCaretPositionFromStringIndex(stringPositionInternal);
            m_CaretSelectPosition = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
            if (m_SoftKeyboard != null && compositionLength == 0)
            {
                int stringPosition = m_CaretPosition < m_CaretSelectPosition ? textInfo.characterInfo[m_CaretPosition].index : textInfo.characterInfo[m_CaretSelectPosition].index;
                int length = m_CaretPosition < m_CaretSelectPosition ? stringSelectPositionInternal - stringPosition : stringPositionInternal - stringPosition;
                m_SoftKeyboard.selection = new RangeInt(stringPosition, length);
            }
            // Adjust text RectTranform position to make sure it is visible in viewport.
            Vector2 caretPosition;
            float height = 0;
            if (m_CaretSelectPosition < textInfo.characterCount)
            {
                caretPosition = new Vector2(textInfo.characterInfo[m_CaretSelectPosition].origin, textInfo.characterInfo[m_CaretSelectPosition].descender);
                height = textInfo.characterInfo[m_CaretSelectPosition].ascender - textInfo.characterInfo[m_CaretSelectPosition].descender;
            }
            else
            {
                caretPosition = new Vector2(textInfo.characterInfo[m_CaretSelectPosition - 1].xAdvance, textInfo.characterInfo[m_CaretSelectPosition - 1].descender);
                height = textInfo.characterInfo[m_CaretSelectPosition - 1].ascender - textInfo.characterInfo[m_CaretSelectPosition - 1].descender;
            }
            // TODO: Don't adjust the position of the RectTransform if Reset On Deactivation is disabled
            // and we just selected the Input Field again.
            AdjustRectTransformRelativeToViewport(caretPosition, height, true);
            int startChar = Mathf.Max(0, m_CaretPosition);
            int endChar = Mathf.Max(0, m_CaretSelectPosition);
            // Ensure pos is always less then selPos to make the code simpler
            if (startChar > endChar)
            {
                int temp = startChar;
                startChar = endChar;
                endChar = temp;
            }
            endChar -= 1;
            //Debug.Log("Updating Highlight... Caret Position: " + startChar + " Caret Select POS: " + endChar);
            int currentLineIndex = textInfo.characterInfo[startChar].lineNumber;
            int nextLineStartIdx = textInfo.lineInfo[currentLineIndex].lastCharacterIndex;
            UIVertex vert = UIVertex.simpleVert;
            vert.uv0 = Vector2.zero;
            vert.color = selectionColor;
            int currentChar = startChar;
            while (currentChar <= endChar && currentChar < textInfo.characterCount)
            {
                if (currentChar == nextLineStartIdx || currentChar == endChar)
                {
                    TMP_CharacterInfo startCharInfo = textInfo.characterInfo[startChar];
                    TMP_CharacterInfo endCharInfo = textInfo.characterInfo[currentChar];
                    // Extra check to handle Carriage Return
                    if (currentChar > 0 && endCharInfo.character == '\n' && textInfo.characterInfo[currentChar - 1].character == '\r')
                        endCharInfo = textInfo.characterInfo[currentChar - 1];
                    Vector2 startPosition = new Vector2(startCharInfo.origin, textInfo.lineInfo[currentLineIndex].ascender);
                    Vector2 endPosition = new Vector2(endCharInfo.xAdvance, textInfo.lineInfo[currentLineIndex].descender);
                    var startIndex = vbo.currentVertCount;
                    vert.position = new Vector3(startPosition.x, endPosition.y, 0.0f);
                    vbo.AddVert(vert);
                    vert.position = new Vector3(endPosition.x, endPosition.y, 0.0f);
                    vbo.AddVert(vert);
                    vert.position = new Vector3(endPosition.x, startPosition.y, 0.0f);
                    vbo.AddVert(vert);
                    vert.position = new Vector3(startPosition.x, startPosition.y, 0.0f);
                    vbo.AddVert(vert);
                    vbo.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
                    vbo.AddTriangle(startIndex + 2, startIndex + 3, startIndex + 0);
                    startChar = currentChar + 1;
                    currentLineIndex++;
                    if (currentLineIndex < textInfo.lineCount)
                        nextLineStartIdx = textInfo.lineInfo[currentLineIndex].lastCharacterIndex;
                }
                currentChar++;
            }
            //#if TMP_DEBUG_MODE
            //    Debug.Log("Text selection updated at frame: " + Time.frameCount);
            //#endif
        }
        /// 
        ///
        /// 
        /// 
        /// 
        /// 
        private void AdjustRectTransformRelativeToViewport(Vector2 startPosition, float height, bool isCharVisible)
        {
            //Debug.Log("Adjusting transform position relative to viewport.");
            if (m_TextViewport == null)
                return;
            Vector3 localPosition = transform.localPosition;
            Vector3 textComponentLocalPosition = m_TextComponent.rectTransform.localPosition;
            Vector3 textViewportLocalPosition = m_TextViewport.localPosition;
            Rect textViewportRect = m_TextViewport.rect;
            Vector2 caretPosition = new Vector2(startPosition.x + textComponentLocalPosition.x + textViewportLocalPosition.x + localPosition.x, startPosition.y + textComponentLocalPosition.y + textViewportLocalPosition.y + localPosition.y);
            Rect viewportWSRect = new Rect(localPosition.x + textViewportLocalPosition.x + textViewportRect.x, localPosition.y + textViewportLocalPosition.y + textViewportRect.y, textViewportRect.width, textViewportRect.height);
            // Adjust the position of the RectTransform based on the caret position in the viewport.
            float rightOffset = viewportWSRect.xMax - (caretPosition.x + m_TextComponent.margin.z + m_CaretWidth);
            if (rightOffset < 0f)
            {
                if (!multiLine || (multiLine && isCharVisible))
                {
                    //Debug.Log("Shifting text to the LEFT by " + rightOffset.ToString("f3"));
                    m_TextComponent.rectTransform.anchoredPosition += new Vector2(rightOffset, 0);
                    AssignPositioningIfNeeded();
                }
            }
            float leftOffset = (caretPosition.x - m_TextComponent.margin.x) - viewportWSRect.xMin;
            if (leftOffset < 0f)
            {
                //Debug.Log("Shifting text to the RIGHT by " + leftOffset.ToString("f3"));
                m_TextComponent.rectTransform.anchoredPosition += new Vector2(-leftOffset, 0);
                AssignPositioningIfNeeded();
            }
            // Adjust text area up or down if not in single line mode.
            if (m_LineType != LineType.SingleLine)
            {
                float topOffset = viewportWSRect.yMax - (caretPosition.y + height);
                if (topOffset < -0.0001f)
                {
                    //Debug.Log("Shifting text to Up " + topOffset.ToString("f3"));
                    m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, topOffset);
                    AssignPositioningIfNeeded();
                }
                float bottomOffset = caretPosition.y - viewportWSRect.yMin;
                if (bottomOffset < 0f)
                {
                    //Debug.Log("Shifting text to Down " + bottomOffset.ToString("f3"));
                    m_TextComponent.rectTransform.anchoredPosition -= new Vector2(0, bottomOffset);
                    AssignPositioningIfNeeded();
                }
            }
            // Special handling of backspace/text being removed
            if (m_HasTextBeenRemoved)
            {
                float anchoredPositionX = m_TextComponent.rectTransform.anchoredPosition.x;
                float firstCharPosition = localPosition.x + textViewportLocalPosition.x + textComponentLocalPosition.x + m_TextComponent.textInfo.characterInfo[0].origin - m_TextComponent.margin.x;
                int lastCharacterIndex = ClampArrayIndex(m_TextComponent.textInfo.characterCount - 1);
                float lastCharPosition = localPosition.x + textViewportLocalPosition.x + textComponentLocalPosition.x + m_TextComponent.textInfo.characterInfo[lastCharacterIndex].origin + m_TextComponent.margin.z + m_CaretWidth;
                if (anchoredPositionX > 0.0001f && firstCharPosition > viewportWSRect.xMin)
                {
                    float offset = viewportWSRect.xMin - firstCharPosition;
                    if (anchoredPositionX < -offset)
                        offset = -anchoredPositionX;
                    m_TextComponent.rectTransform.anchoredPosition += new Vector2(offset, 0);
                    AssignPositioningIfNeeded();
                }
                else if (anchoredPositionX < -0.0001f && lastCharPosition < viewportWSRect.xMax)
                {
                    float offset = viewportWSRect.xMax - lastCharPosition;
                    if (-anchoredPositionX < offset)
                        offset = -anchoredPositionX;
                    m_TextComponent.rectTransform.anchoredPosition += new Vector2(offset, 0);
                    AssignPositioningIfNeeded();
                }
                m_HasTextBeenRemoved = false;
            }
            m_forceRectTransformAdjustment = false;
        }
        /// 
        /// Validate the specified input.
        /// 
        protected char Validate(string text, int pos, char ch)
        {
            // Validation is disabled
            if (characterValidation == CharacterValidation.None || !enabled)
                return ch;
            if (characterValidation == CharacterValidation.Integer || characterValidation == CharacterValidation.Decimal)
            {
                // Integer and decimal
                bool cursorBeforeDash = (pos == 0 && text.Length > 0 && text[0] == '-');
                bool selectionAtStart = stringPositionInternal == 0 || stringSelectPositionInternal == 0;
                if (!cursorBeforeDash)
                {
                    if (ch >= '0' && ch <= '9') return ch;
                    if (ch == '-' && (pos == 0 || selectionAtStart) && !text.Contains('-')) return ch;
                    var separator = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
                    if (ch == Convert.ToChar(separator) && characterValidation == CharacterValidation.Decimal && !text.Contains(separator)) return ch;
                    //Some keyboards including Samsung require double tapping a . to get a - this allows these keyboards to input negative integers
                    if (characterValidation == CharacterValidation.Integer && ch == '.' && (pos == 0 || selectionAtStart) && !text.Contains('-')) return '-';
                }
            }
            else if (characterValidation == CharacterValidation.Digit)
            {
                if (ch >= '0' && ch <= '9') return ch;
            }
            else if (characterValidation == CharacterValidation.Alphanumeric)
            {
                // All alphanumeric characters
                if (ch >= 'A' && ch <= 'Z') return ch;
                if (ch >= 'a' && ch <= 'z') return ch;
                if (ch >= '0' && ch <= '9') return ch;
            }
            else if (characterValidation == CharacterValidation.Name)
            {
                char prevChar = (text.Length > 0) ? text[Mathf.Clamp(pos - 1, 0, text.Length - 1)] : ' ';
                char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
                char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
                if (char.IsLetter(ch))
                {
                    // First letter is always capitalized
                    if (char.IsLower(ch) && pos == 0)
                        return char.ToUpper(ch);
                    // Letter following a space or hyphen is always capitalized
                    if (char.IsLower(ch) && (prevChar == ' ' || prevChar == '-'))
                        return char.ToUpper(ch);
                    // Uppercase letters are only allowed after spaces, apostrophes, hyphens or lowercase letter
                    if (char.IsUpper(ch) && pos > 0 && prevChar != ' ' && prevChar != '\'' && prevChar != '-' && !char.IsLower(prevChar))
                        return char.ToLower(ch);
                    // Do not allow uppercase characters to be inserted before another uppercase character
                    if (char.IsUpper(ch) && char.IsUpper(lastChar))
                        return (char)0;
                    // If character was already in correct case, return it as-is.
                    // Also, letters that are neither upper nor lower case are always allowed.
                    return ch;
                }
                else if (ch == '\'')
                {
                    // Don't allow more than one apostrophe
                    if (lastChar != ' ' && lastChar != '\'' && nextChar != '\'' && !text.Contains("'"))
                        return ch;
                }
                // Allow inserting a hyphen after a character
                if (char.IsLetter(prevChar) && ch == '-' && lastChar != '-')
                {
                    return ch;
                }
                if ((ch == ' ' || ch == '-') && pos != 0)
                {
                    // Don't allow more than one space in a row
                    if (prevChar != ' ' && prevChar != '\'' && prevChar != '-' &&
                        lastChar != ' ' && lastChar != '\'' && lastChar != '-' &&
                        nextChar != ' ' && nextChar != '\'' && nextChar != '-')
                        return ch;
                }
            }
            else if (characterValidation == CharacterValidation.EmailAddress)
            {
                // From StackOverflow about allowed characters in email addresses:
                // Uppercase and lowercase English letters (a-z, A-Z)
                // Digits 0 to 9
                // Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~
                // Character . (dot, period, full stop) provided that it is not the first or last character,
                // and provided also that it does not appear two or more times consecutively.
                if (ch >= 'A' && ch <= 'Z') return ch;
                if (ch >= 'a' && ch <= 'z') return ch;
                if (ch >= '0' && ch <= '9') return ch;
                if (ch == '@' && text.IndexOf('@') == -1) return ch;
                if (kEmailSpecialCharacters.IndexOf(ch) != -1) return ch;
                if (ch == '.')
                {
                    char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
                    char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
                    if (lastChar != '.' && nextChar != '.')
                        return ch;
                }
            }
            else if (characterValidation == CharacterValidation.Regex)
            {
                // Regex expression
                if (Regex.IsMatch(ch.ToString(), m_RegexValue))
                {
                    return ch;
                }
            }
            else if (characterValidation == CharacterValidation.CustomValidator)
            {
                if (m_InputValidator != null)
                {
                    char c = m_InputValidator.Validate(ref text, ref pos, ch);
                    m_Text = text;
                    stringSelectPositionInternal = stringPositionInternal = pos;
                    return c;
                }
            }
            return (char)0;
        }
        public void ActivateInputField()
        {
            if (m_TextComponent == null || m_TextComponent.font == null || !IsActive() || !IsInteractable())
                return;
            if (isFocused)
            {
                if (m_SoftKeyboard != null && !m_SoftKeyboard.active)
                {
                    m_SoftKeyboard.active = true;
                    m_SoftKeyboard.text = m_Text;
                }
            }
            m_ShouldActivateNextUpdate = true;
        }
        private void ActivateInputFieldInternal()
        {
            if (EventSystem.current == null)
                return;
            if (EventSystem.current.currentSelectedGameObject != gameObject)
                EventSystem.current.SetSelectedGameObject(gameObject);
            // Cache the value of isInPlaceEditingAllowed, because on UWP this involves calling into native code
            // The value only needs to be updated once when the TouchKeyboard is opened.
            m_TouchKeyboardAllowsInPlaceEditing = !s_IsQuestDevice && TouchScreenKeyboard.isInPlaceEditingAllowed;
            if (TouchScreenKeyboardShouldBeUsed() && shouldHideSoftKeyboard == false)
            {
                if (inputSystem != null && inputSystem.touchSupported)
                {
                    TouchScreenKeyboard.hideInput = shouldHideMobileInput;
                }
                if (shouldHideSoftKeyboard == false && m_ReadOnly == false)
                {
                    m_SoftKeyboard = (inputType == InputType.Password) ?
                        TouchScreenKeyboard.Open(m_Text, keyboardType, false, multiLine, true, isAlert, "", characterLimit) :
                        TouchScreenKeyboard.Open(m_Text, keyboardType, inputType == InputType.AutoCorrect, multiLine, false, isAlert, "", characterLimit);
                    OnFocus();
                    // Opening the soft keyboard sets its selection to the end of the text.
                    // As such, we set the selection to match the Input Field's internal selection. // UUM-112457
                    if (m_SoftKeyboard != null)
                    {
                        int length = stringPositionInternal < stringSelectPositionInternal ? stringSelectPositionInternal - stringPositionInternal : stringPositionInternal - stringSelectPositionInternal;
                        m_SoftKeyboard.selection = new RangeInt(stringPositionInternal < stringSelectPositionInternal ? stringPositionInternal : stringSelectPositionInternal, length);
                    }
                    //}
                }
            }
            else
            {
                if (!TouchScreenKeyboardShouldBeUsed() && m_ReadOnly == false && inputSystem != null)
                    inputSystem.imeCompositionMode = IMECompositionMode.On;
                OnFocus();
            }
            m_AllowInput = true;
            m_OriginalText = text;
            m_WasCanceled = false;
            SetCaretVisible();
            UpdateLabel();
        }
        public override void OnSelect(BaseEventData eventData)
        {
            //Debug.Log("OnSelect()");
            base.OnSelect(eventData);
            SendOnFocus();
            if (shouldActivateOnSelect)
                ActivateInputField();
        }
        public virtual void OnPointerClick(PointerEventData eventData)
        {
            //Debug.Log("Pointer Click Event...");
            if (eventData.button != PointerEventData.InputButton.Left)
                return;
            ActivateInputField();
        }
        public void OnControlClick()
        {
            //Debug.Log("Input Field control click...");
        }
        public void ReleaseSelection()
        {
            m_SelectionStillActive = false;
            m_ReleaseSelection = false;
            m_PreviouslySelectedObject = null;
            MarkGeometryAsDirty();
            SendOnEndEdit();
            SendOnEndTextSelection();
        }
        public void DeactivateInputField(bool clearSelection = false)
        {
            //Debug.Log("Deactivate Input Field...");
            // Not activated do nothing.
            if (!m_AllowInput)
                return;
            m_HasDoneFocusTransition = false;
            m_AllowInput = false;
            if (m_Placeholder != null)
                m_Placeholder.enabled = string.IsNullOrEmpty(m_Text);
            if (m_TextComponent != null && IsInteractable())
            {
                if (m_WasCanceled && m_RestoreOriginalTextOnEscape && !m_IsKeyboardBeingClosedInHoloLens)
                    text = m_OriginalText;
                if (m_SoftKeyboard != null)
                {
                    m_SoftKeyboard.active = false;
                    m_SoftKeyboard = null;
                }
                m_SelectionStillActive = true;
                if (m_ResetOnDeActivation || m_ReleaseSelection || clearSelection)
                {
                    //m_StringPosition = m_StringSelectPosition = 0;
                    //m_CaretPosition = m_CaretSelectPosition = 0;
                    //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
                    if (m_VerticalScrollbar == null)
                        ReleaseSelection();
                }
                if (inputSystem != null)
                    inputSystem.imeCompositionMode = IMECompositionMode.Auto;
				m_IsKeyboardBeingClosedInHoloLens = false;
            }
            MarkGeometryAsDirty();
        }
        public override void OnDeselect(BaseEventData eventData)
        {
            DeactivateInputField();
            base.OnDeselect(eventData);
            SendOnFocusLost();
        }
        public virtual void OnSubmit(BaseEventData eventData)
        {
            //Debug.Log("OnSubmit()");
            if (!IsActive() || !IsInteractable())
                return;
            if (!isFocused)
                m_ShouldActivateNextUpdate = true;
            SendOnSubmit();
            DeactivateInputField();
            eventData?.Use();
        }
        public virtual void OnCancel(BaseEventData eventData)
        {
            if (!IsActive() || !IsInteractable())
                return;
            if (!isFocused)
                m_ShouldActivateNextUpdate = true;
            m_WasCanceled = true;
            DeactivateInputField();
            eventData.Use();
        }
        public override void OnMove(AxisEventData eventData)
        {
            // Prevent UI navigation while text is being edited.
            if (!m_AllowInput)
                base.OnMove(eventData);
        }
        //public virtual void OnLostFocus(BaseEventData eventData)
        //{
        //    if (!IsActive() || !IsInteractable())
        //        return;
        //}
        private void EnforceContentType()
        {
            switch (contentType)
            {
                case ContentType.Standard:
                    {
                        // Don't enforce line type for this content type.
                        m_InputType = InputType.Standard;
                        m_KeyboardType = TouchScreenKeyboardType.Default;
                        m_CharacterValidation = CharacterValidation.None;
                        break;
                    }
                case ContentType.Autocorrected:
                    {
                        // Don't enforce line type for this content type.
                        m_InputType = InputType.AutoCorrect;
                        m_KeyboardType = TouchScreenKeyboardType.Default;
                        m_CharacterValidation = CharacterValidation.None;
                        break;
                    }
                case ContentType.IntegerNumber:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Standard;
                        m_KeyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
                        m_CharacterValidation = CharacterValidation.Integer;
                        break;
                    }
                case ContentType.DecimalNumber:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Standard;
                        m_KeyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
                        m_CharacterValidation = CharacterValidation.Decimal;
                        break;
                    }
                case ContentType.Alphanumeric:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Standard;
                        m_KeyboardType = TouchScreenKeyboardType.ASCIICapable;
                        m_CharacterValidation = CharacterValidation.Alphanumeric;
                        break;
                    }
                case ContentType.Name:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Standard;
                        m_KeyboardType = TouchScreenKeyboardType.Default;
                        m_CharacterValidation = CharacterValidation.Name;
                        break;
                    }
                case ContentType.EmailAddress:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Standard;
                        m_KeyboardType = TouchScreenKeyboardType.EmailAddress;
                        m_CharacterValidation = CharacterValidation.EmailAddress;
                        break;
                    }
                case ContentType.Password:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Password;
                        m_KeyboardType = TouchScreenKeyboardType.Default;
                        m_CharacterValidation = CharacterValidation.None;
                        break;
                    }
                case ContentType.Pin:
                    {
                        m_LineType = LineType.SingleLine;
                        m_InputType = InputType.Password;
                        m_KeyboardType = TouchScreenKeyboardType.NumberPad;
                        m_CharacterValidation = CharacterValidation.Digit;
                        break;
                    }
                default:
                    {
                        // Includes Custom type. Nothing should be enforced.
                        break;
                    }
            }
            SetTextComponentWrapMode();
        }
        void SetTextComponentWrapMode()
        {
            if (m_TextComponent == null)
                return;
            if (multiLine)
                m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
            else
                m_TextComponent.textWrappingMode = TextWrappingModes.PreserveWhitespaceNoWrap;
        }
        // Control Rich Text option on the text component.
        void SetTextComponentRichTextMode()
        {
            if (m_TextComponent == null)
                return;
            m_TextComponent.richText = m_RichText;
        }
        void SetToCustomIfContentTypeIsNot(params ContentType[] allowedContentTypes)
        {
            if (contentType == ContentType.Custom)
                return;
            for (int i = 0; i < allowedContentTypes.Length; i++)
                if (contentType == allowedContentTypes[i])
                    return;
            contentType = ContentType.Custom;
        }
        void SetToCustom()
        {
            if (contentType == ContentType.Custom)
                return;
            contentType = ContentType.Custom;
        }
        void SetToCustom(CharacterValidation characterValidation)
        {
            if (contentType == ContentType.Custom)
            {
                characterValidation = CharacterValidation.CustomValidator;
                return;
            }
            contentType = ContentType.Custom;
            characterValidation = CharacterValidation.CustomValidator;
        }
        protected override void DoStateTransition(SelectionState state, bool instant)
        {
            if (m_HasDoneFocusTransition)
                state = SelectionState.Selected;
            else if (state == SelectionState.Pressed)
                m_HasDoneFocusTransition = true;
            base.DoStateTransition(state, instant);
        }
        /// 
        /// See ILayoutElement.CalculateLayoutInputHorizontal.
        /// 
        public virtual void CalculateLayoutInputHorizontal()
        { }
        /// 
        /// See ILayoutElement.CalculateLayoutInputVertical.
        /// 
        public virtual void CalculateLayoutInputVertical()
        { }
        /// 
        /// See ILayoutElement.minWidth.
        /// 
        public virtual float minWidth { get { return 0; } }
        /// 
        /// Get the displayed with of all input characters.
        /// 
        public virtual float preferredWidth
        {
            get
            {
                if (textComponent == null)
                    return 0;
                float horizontalPadding = 0;
                if (m_LayoutGroup != null)
                    horizontalPadding = m_LayoutGroup.padding.horizontal;
                if (m_TextViewport != null)
                    horizontalPadding += m_TextViewport.offsetMin.x - m_TextViewport.offsetMax.x;
                return m_TextComponent.preferredWidth + horizontalPadding; // Should add some extra padding for caret
            }
        }
        /// 
        /// See ILayoutElement.flexibleWidth.
        /// 
        public virtual float flexibleWidth { get { return -1; } }
        /// 
        /// See ILayoutElement.minHeight.
        /// 
        public virtual float minHeight { get { return 0; } }
        /// 
        /// Get the height of all the text if constrained to the height of the RectTransform.
        /// 
        public virtual float preferredHeight
        {
            get
            {
                if (textComponent == null)
                    return 0;
                float verticalPadding = 0;
                if (m_LayoutGroup != null)
                    verticalPadding = m_LayoutGroup.padding.vertical;
                if (m_TextViewport != null)
                    verticalPadding += m_TextViewport.offsetMin.y - m_TextViewport.offsetMax.y;
                return m_TextComponent.preferredHeight + verticalPadding;
            }
        }
        /// 
        /// See ILayoutElement.flexibleHeight.
        /// 
        public virtual float flexibleHeight { get { return -1; } }
        /// 
        /// See ILayoutElement.layoutPriority.
        /// 
        public virtual int layoutPriority { get { return 1; } }
        /// 
        /// Function to conveniently set the point size of both Placeholder and Input Field text object.
        /// 
        /// 
        public void SetGlobalPointSize(float pointSize)
        {
            TMP_Text placeholderTextComponent = m_Placeholder as TMP_Text;
            if (placeholderTextComponent != null)
                placeholderTextComponent.fontSize = pointSize;
            textComponent.fontSize = pointSize;
        }
        /// 
        /// Function to conveniently set the Font Asset of both Placeholder and Input Field text object.
        /// 
        /// 
        public void SetGlobalFontAsset(TMP_FontAsset fontAsset)
        {
            TMP_Text placeholderTextComponent = m_Placeholder as TMP_Text;
            if (placeholderTextComponent != null)
                placeholderTextComponent.font = fontAsset;
            textComponent.font = fontAsset;
        }
    }
    static class SetPropertyUtility
    {
        public static bool SetColor(ref Color currentValue, Color newValue)
        {
            if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a)
                return false;
            currentValue = newValue;
            return true;
        }
        public static bool SetEquatableStruct(ref T currentValue, T newValue) where T : IEquatable
        {
            if (currentValue.Equals(newValue))
                return false;
            currentValue = newValue;
            return true;
        }
        public static bool SetStruct(ref T currentValue, T newValue) where T : struct
        {
            if (currentValue.Equals(newValue))
                return false;
            currentValue = newValue;
            return true;
        }
        public static bool SetClass(ref T currentValue, T newValue) where T : class
        {
            if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
                return false;
            currentValue = newValue;
            return true;
        }
    }
}