//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- #if !UNITY_EDITOR && (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_WP_8_1 || UNITY_BLACKBERRY || UNITY_WINRT || UNITY_METRO) #define MOBILE #endif #define FUNCELL_MODIFIED using UnityEngine; using System.Collections.Generic; using System.Text; /// /// Input field makes it possible to enter custom information within the UI. /// [AddComponentMenu("NGUI/UI/Input Field")] public class UIInput : MonoBehaviour { public enum InputType { Standard, AutoCorrect, Password, } public enum Validation { None, Integer, Float, Alphanumeric, Username, Name, Filename, } #if UNITY_EDITOR public enum KeyboardType { Default = (int)TouchScreenKeyboardType.Default, ASCIICapable = (int)TouchScreenKeyboardType.ASCIICapable, NumbersAndPunctuation = (int)TouchScreenKeyboardType.NumbersAndPunctuation, URL = (int)TouchScreenKeyboardType.URL, NumberPad = (int)TouchScreenKeyboardType.NumberPad, PhonePad = (int)TouchScreenKeyboardType.PhonePad, NamePhonePad = (int)TouchScreenKeyboardType.NamePhonePad, EmailAddress = (int)TouchScreenKeyboardType.EmailAddress, } #else public enum KeyboardType { Default = 0, ASCIICapable = 1, NumbersAndPunctuation = 2, URL = 3, NumberPad = 4, PhonePad = 5, NamePhonePad = 6, EmailAddress = 7, } #endif public enum OnReturnKey { Default, Submit, NewLine, } public delegate char OnValidate (string text, int charIndex, char addedChar); /// /// Currently active input field. Only valid during callbacks. /// static public UIInput current; /// /// Currently selected input field, if any. /// static public UIInput selection; /// /// Text label used to display the input's value. /// public UILabel label; /// /// Type of data expected by the input field. /// public InputType inputType = InputType.Standard; /// /// What to do when the Return key is pressed on the keyboard. /// public OnReturnKey onReturnKey = OnReturnKey.Default; /// /// Keyboard type applies to mobile keyboards that get shown. /// public KeyboardType keyboardType = KeyboardType.Default; /// /// Whether the input will be hidden on mobile platforms. /// public bool hideInput = false; /// /// Whether all text will be selected when the input field gains focus. /// [System.NonSerialized] public bool selectAllTextOnFocus = true; /// /// What kind of validation to use with the input field's data. /// public Validation validation = Validation.None; /// /// Maximum number of characters allowed before input no longer works. /// public int characterLimit = 0; /// /// Field in player prefs used to automatically save the value. /// public string savedAs; /// /// Don't use this anymore. Attach UIKeyNavigation instead. /// [HideInInspector][SerializeField] GameObject selectOnTab; /// /// Color of the label when the input field has focus. /// public Color activeTextColor = Color.white; /// /// Color used by the caret symbol. /// public Color caretColor = new Color(1f, 1f, 1f, 0.8f); /// /// Color used by the selection rectangle. /// public Color selectionColor = new Color(1f, 223f / 255f, 141f / 255f, 0.5f); /// /// Event delegates triggered when the input field submits its data. /// public List onSubmit = new List(); /// /// Event delegates triggered when the input field's text changes for any reason. /// public List onChange = new List(); /// /// Custom validation callback. /// public OnValidate onValidate; /// /// Input field's value. /// [SerializeField][HideInInspector] protected string mValue; [System.NonSerialized] protected string mDefaultText = ""; [System.NonSerialized] protected Color mDefaultColor = Color.white; [System.NonSerialized] protected float mPosition = 0f; [System.NonSerialized] protected bool mDoInit = true; [System.NonSerialized] protected NGUIText.Alignment mAlignment = NGUIText.Alignment.Left; [System.NonSerialized] protected bool mLoadSavedValue = true; static protected int mDrawStart = 0; static protected string mLastIME = ""; #if MOBILE // Unity fails to compile if the touch screen keyboard is used on a non-mobile device static protected TouchScreenKeyboard mKeyboard; static bool mWaitForKeyboard = false; #endif [System.NonSerialized] protected int mSelectionStart = 0; [System.NonSerialized] protected int mSelectionEnd = 0; [System.NonSerialized] protected UITexture mHighlight = null; [System.NonSerialized] protected UITexture mCaret = null; [System.NonSerialized] protected Texture2D mBlankTex = null; [System.NonSerialized] protected float mNextBlink = 0f; [System.NonSerialized] protected float mLastAlpha = 0f; [System.NonSerialized] protected string mCached = ""; [System.NonSerialized] protected int mSelectMe = -1; [System.NonSerialized] protected int mSelectTime = -1; /// /// Default text used by the input's label. /// public string defaultText { get { if (mDoInit) Init(); return mDefaultText; } set { if (mDoInit) Init(); mDefaultText = value; UpdateLabel(); } } /// /// Text's default color when not selected. /// public Color defaultColor { get { if (mDoInit) Init(); return mDefaultColor; } set { mDefaultColor = value; if (!isSelected) label.color = value; } } /// /// Should the input be hidden? /// public bool inputShouldBeHidden { get { return hideInput && label != null && !label.multiLine && inputType != InputType.Password; } } [System.Obsolete("Use UIInput.value instead")] public string text { get { return this.value; } set { this.value = value; } } /// /// Input field's current text value. /// public string value { get { #if UNITY_EDITOR if (!Application.isPlaying) return ""; #endif if (mDoInit) Init(); return mValue; } set { #if UNITY_EDITOR if (!Application.isPlaying) return; #endif if (mDoInit) Init(); mDrawStart = 0; // BB10's implementation has a bug in Unity #if UNITY_4_3 if (Application.platform == RuntimePlatform.BB10Player) #else if (Application.platform == RuntimePlatform.BlackBerryPlayer) #endif value = value.Replace("\\b", "\b"); // Validate all input value = Validate(value); #if MOBILE if (isSelected && mKeyboard != null && mCached != value) { mKeyboard.text = value; mCached = value; } #endif if (mValue != value) { mValue = value; mLoadSavedValue = false; if (isSelected) { if (string.IsNullOrEmpty(value)) { mSelectionStart = 0; mSelectionEnd = 0; } else { mSelectionStart = value.Length; mSelectionEnd = mSelectionStart; } } else SaveToPlayerPrefs(value); UpdateLabel(); ExecuteOnChange(); } } } [System.Obsolete("Use UIInput.isSelected instead")] public bool selected { get { return isSelected; } set { isSelected = value; } } /// /// Whether the input is currently selected. /// public bool isSelected { get { return selection == this; } set { if (!value) { if (isSelected) UICamera.selectedObject = null; } else UICamera.selectedObject = gameObject; } } /// /// Current position of the cursor. /// public int cursorPosition { get { #if MOBILE if (mKeyboard != null && !inputShouldBeHidden) return value.Length; #endif return isSelected ? mSelectionEnd : value.Length; } set { if (isSelected) { #if MOBILE if (mKeyboard != null && !inputShouldBeHidden) return; #endif mSelectionEnd = value; UpdateLabel(); } } } /// /// Index of the character where selection begins. /// public int selectionStart { get { #if MOBILE if (mKeyboard != null && !inputShouldBeHidden) return 0; #endif return isSelected ? mSelectionStart : value.Length; } set { if (isSelected) { #if MOBILE if (mKeyboard != null && !inputShouldBeHidden) return; #endif mSelectionStart = value; UpdateLabel(); } } } /// /// Index of the character where selection ends. /// public int selectionEnd { get { #if MOBILE if (mKeyboard != null && !inputShouldBeHidden) return value.Length; #endif return isSelected ? mSelectionEnd : value.Length; } set { if (isSelected) { #if MOBILE if (mKeyboard != null && !inputShouldBeHidden) return; #endif mSelectionEnd = value; UpdateLabel(); } } } /// /// Caret, in case it's needed. /// public UITexture caret { get { return mCaret; } } /// /// Validate the specified text, returning the validated version. /// public string Validate (string val) { if (string.IsNullOrEmpty(val)) return ""; StringBuilder sb = new StringBuilder(val.Length); for (int i = 0; i < val.Length; ++i) { char c = val[i]; if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c); else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c); if (c != 0) sb.Append(c); } if (characterLimit > 0 && sb.Length > characterLimit) return sb.ToString(0, characterLimit); return sb.ToString(); } /// /// Automatically set the value by loading it from player prefs if possible. /// void Start () { if (selectOnTab != null) { UIKeyNavigation nav = GetComponent(); if (nav == null) { nav = gameObject.AddComponent(); nav.onDown = selectOnTab; } selectOnTab = null; NGUITools.SetDirty(this); } if (mLoadSavedValue && !string.IsNullOrEmpty(savedAs)) LoadValue(); else value = mValue.Replace("\\n", "\n"); } /// /// Labels used for input shouldn't support rich text. /// protected void Init () { if (mDoInit && label != null) { mDoInit = false; mDefaultText = label.text; mDefaultColor = label.color; label.supportEncoding = false; mEllipsis = label.overflowEllipsis; if (label.alignment == NGUIText.Alignment.Justified) { label.alignment = NGUIText.Alignment.Left; Debug.LogWarning("Input fields using labels with justified alignment are not supported at this time", this); } mAlignment = label.alignment; mPosition = label.cachedTransform.localPosition.x; UpdateLabel(); } } /// /// Save the specified value to player prefs. /// protected void SaveToPlayerPrefs (string val) { if (!string.IsNullOrEmpty(savedAs)) { if (string.IsNullOrEmpty(val)) PlayerPrefs.DeleteKey(savedAs); else PlayerPrefs.SetString(savedAs, val); } } #if !MOBILE [System.NonSerialized] UIInputOnGUI mOnGUI; #endif [System.NonSerialized] UICamera mCam; /// /// Selection event, sent by the EventSystem. /// protected virtual void OnSelect (bool isSelected) { if (isSelected) { #if !MOBILE if (mOnGUI == null) mOnGUI = gameObject.AddComponent(); #endif OnSelectEvent(); } else { #if !MOBILE if (mOnGUI != null) { Destroy(mOnGUI); mOnGUI = null; } #endif OnDeselectEvent(); } } /// /// Notification of the input field gaining selection. /// protected void OnSelectEvent () { mSelectTime = Time.frameCount; selection = this; if (mDoInit) Init(); if (label != null) { mEllipsis = label.overflowEllipsis; label.overflowEllipsis = false; } // Unity has issues bringing up the keyboard properly if it's in "hideInput" mode and you happen // to select one input in the same Update as de-selecting another. if (label != null && NGUITools.GetActive(this)) mSelectMe = Time.frameCount; } [System.NonSerialized] bool mEllipsis = false; /// /// Notification of the input field losing selection. /// protected void OnDeselectEvent () { if (mDoInit) Init(); if (label != null) label.overflowEllipsis = mEllipsis; if (label != null && NGUITools.GetActive(this)) { mValue = value; #if MOBILE if (mKeyboard != null) { mWaitForKeyboard = false; mKeyboard.active = false; mKeyboard = null; } #endif if (string.IsNullOrEmpty(mValue)) { label.text = mDefaultText; label.color = mDefaultColor; } else label.text = mValue; Input.imeCompositionMode = IMECompositionMode.Auto; label.alignment = mAlignment; } selection = null; UpdateLabel(); } /// /// Update the text based on input. /// protected virtual void Update () { #if UNITY_EDITOR if (!Application.isPlaying) return; #endif if (!isSelected || mSelectTime == Time.frameCount) return; if (mDoInit) Init(); #if MOBILE // Wait for the keyboard to open. Apparently mKeyboard.active will return 'false' for a while in some cases. if (mWaitForKeyboard) { if (mKeyboard != null && !mKeyboard.active) return; mWaitForKeyboard = false; } #endif // Unity has issues bringing up the keyboard properly if it's in "hideInput" mode and you happen // to select one input in the same Update as de-selecting another. if (mSelectMe != -1 && mSelectMe != Time.frameCount) { mSelectMe = -1; mSelectionEnd = string.IsNullOrEmpty(mValue) ? 0 : mValue.Length; mDrawStart = 0; mSelectionStart = selectAllTextOnFocus ? 0 : mSelectionEnd; label.color = activeTextColor; #if MOBILE RuntimePlatform pf = Application.platform; if (pf == RuntimePlatform.IPhonePlayer || pf == RuntimePlatform.Android || pf == RuntimePlatform.WP8Player #if UNITY_4_3 || pf == RuntimePlatform.BB10Player #else || pf == RuntimePlatform.BlackBerryPlayer || pf == RuntimePlatform.MetroPlayerARM || pf == RuntimePlatform.MetroPlayerX64 || pf == RuntimePlatform.MetroPlayerX86 #endif ) { string val; TouchScreenKeyboardType kt; if (inputShouldBeHidden) { TouchScreenKeyboard.hideInput = true; kt = (TouchScreenKeyboardType)((int)keyboardType); val = "|"; } else if (inputType == InputType.Password) { TouchScreenKeyboard.hideInput = false; kt = (TouchScreenKeyboardType)((int)keyboardType); val = mValue; mSelectionStart = mSelectionEnd; } else { TouchScreenKeyboard.hideInput = false; kt = (TouchScreenKeyboardType)((int)keyboardType); val = mValue; mSelectionStart = mSelectionEnd; } mWaitForKeyboard = true; mKeyboard = (inputType == InputType.Password) ? TouchScreenKeyboard.Open(val, kt, false, false, true) : TouchScreenKeyboard.Open(val, kt, !inputShouldBeHidden && inputType == InputType.AutoCorrect, label.multiLine && !hideInput, false, false, defaultText); #if UNITY_METRO mKeyboard.active = true; #endif } else #endif // MOBILE { Vector2 pos = (UICamera.current != null && UICamera.current.cachedCamera != null) ? UICamera.current.cachedCamera.WorldToScreenPoint(label.worldCorners[0]) : label.worldCorners[0]; pos.y = Screen.height - pos.y; Input.imeCompositionMode = IMECompositionMode.On; Input.compositionCursorPos = pos; } UpdateLabel(); if (string.IsNullOrEmpty(Input.inputString)) return; } #if MOBILE if (mKeyboard != null) { string text = (mKeyboard.done || !mKeyboard.active) ? mCached : mKeyboard.text; if (inputShouldBeHidden) { if (text != "|") { if (!string.IsNullOrEmpty(text)) { Insert(text.Substring(1)); } else if (!mKeyboard.done && mKeyboard.active) { DoBackspace(); } mKeyboard.text = "|"; } } else if (mCached != text) { mCached = text; if (!mKeyboard.done && mKeyboard.active) value = text; } if (mKeyboard.done || !mKeyboard.active) { if (!mKeyboard.wasCanceled) Submit(); mKeyboard = null; isSelected = false; mCached = ""; } } else #endif // MOBILE { string ime = Input.compositionString; // There seems to be an inconsistency between IME on Windows, and IME on OSX. // On Windows, Input.inputString is always empty while IME is active. On the OSX it is not. #if UNITY_STANDALONE_WIN && FUNCELL_MODIFIED //Fix UIInput problem on Korean character, 韩国pc版只能打单字 if (!string.IsNullOrEmpty(Input.inputString)) #else if (string.IsNullOrEmpty(ime) && !string.IsNullOrEmpty(Input.inputString)) #endif { // Process input ignoring non-printable characters as they are not consistent. // Windows has them, OSX may not. They get handled inside OnGUI() instead. string s = Input.inputString; for (int i = 0; i < s.Length; ++i) { char ch = s[i]; if (ch < ' ') continue; // OSX inserts these characters for arrow keys if (ch == '\uF700') continue; if (ch == '\uF701') continue; if (ch == '\uF702') continue; if (ch == '\uF703') continue; Insert(ch.ToString()); } } // Append IME composition if (mLastIME != ime) { mSelectionEnd = string.IsNullOrEmpty(ime) ? mSelectionStart : mValue.Length + ime.Length; mLastIME = ime; UpdateLabel(); ExecuteOnChange(); } } //// Blink the caret //if (mCaret != null && mNextBlink < RealTime.time) //{ // mNextBlink = RealTime.time + 0.5f; // mCaret.enabled = !mCaret.enabled; //} // If the label's final alpha changes, we need to update the drawn geometry, // or the highlight widgets (which have their geometry set manually) won't update. if (isSelected && mLastAlpha != label.finalAlpha) UpdateLabel(); // Cache the camera if (mCam == null) mCam = UICamera.FindCameraForLayer(gameObject.layer); // Having this in OnGUI causes issues because Input.inputString gets updated *after* OnGUI, apparently... if (mCam != null) { bool newLine = false; if (label.multiLine) { bool ctrl = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); if (onReturnKey == OnReturnKey.Submit) newLine = ctrl; else newLine = !ctrl; } if (UICamera.GetKeyDown(mCam.submitKey0)) { if (newLine) { Insert("\n"); } else { if (UICamera.controller.current != null) UICamera.controller.clickNotification = UICamera.ClickNotification.None; UICamera.currentKey = mCam.submitKey0; Submit(); } } if (UICamera.GetKeyDown(mCam.submitKey1)) { if (newLine) { Insert("\n"); } else { if (UICamera.controller.current != null) UICamera.controller.clickNotification = UICamera.ClickNotification.None; UICamera.currentKey = mCam.submitKey1; Submit(); } } if (!mCam.useKeyboard && UICamera.GetKeyUp(KeyCode.Tab)) OnKey(KeyCode.Tab); } } static int mIgnoreKey = 0; void OnKey (KeyCode key) { int frame = Time.frameCount; if (mIgnoreKey == frame) return; if (mCam != null && (key == mCam.cancelKey0 || key == mCam.cancelKey1)) { mIgnoreKey = frame; isSelected = false; } else if (key == KeyCode.Tab) { mIgnoreKey = frame; isSelected = false; UIKeyNavigation nav = GetComponent(); if (nav != null) nav.OnKey(KeyCode.Tab); } } /// /// Perform a backspace operation. /// protected void DoBackspace () { if (!string.IsNullOrEmpty(mValue)) { if (mSelectionStart == mSelectionEnd) { if (mSelectionStart < 1) return; --mSelectionEnd; } Insert(""); } } #if !MOBILE /// /// Handle the specified event. /// public virtual bool ProcessEvent (Event ev) { if (label == null) return false; RuntimePlatform rp = Application.platform; bool isMac = ( rp == RuntimePlatform.OSXEditor || rp == RuntimePlatform.OSXPlayer); bool ctrl = isMac ? ((ev.modifiers & EventModifiers.Command) != 0) : ((ev.modifiers & EventModifiers.Control) != 0); // http://www.tasharen.com/forum/index.php?topic=10780.0 if ((ev.modifiers & EventModifiers.Alt) != 0) ctrl = false; bool shift = ((ev.modifiers & EventModifiers.Shift) != 0); switch (ev.keyCode) { case KeyCode.Backspace: { ev.Use(); DoBackspace(); return true; } case KeyCode.Delete: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { if (mSelectionStart == mSelectionEnd) { if (mSelectionStart >= mValue.Length) return true; ++mSelectionEnd; } Insert(""); } return true; } case KeyCode.LeftArrow: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { mSelectionEnd = Mathf.Max(mSelectionEnd - 1, 0); if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.RightArrow: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { mSelectionEnd = Mathf.Min(mSelectionEnd + 1, mValue.Length); if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.PageUp: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { mSelectionEnd = 0; if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.PageDown: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { mSelectionEnd = mValue.Length; if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.Home: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { if (label.multiLine) { mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.Home); } else mSelectionEnd = 0; if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.End: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { if (label.multiLine) { mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.End); } else mSelectionEnd = mValue.Length; if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.UpArrow: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.UpArrow); if (mSelectionEnd != 0) mSelectionEnd += mDrawStart; if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } case KeyCode.DownArrow: { ev.Use(); if (!string.IsNullOrEmpty(mValue)) { mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.DownArrow); if (mSelectionEnd != label.processedText.Length) mSelectionEnd += mDrawStart; else mSelectionEnd = mValue.Length; if (!shift) mSelectionStart = mSelectionEnd; UpdateLabel(); } return true; } // Select all case KeyCode.A: { if (ctrl) { ev.Use(); mSelectionStart = 0; mSelectionEnd = mValue.Length; UpdateLabel(); } return true; } // Copy case KeyCode.C: { if (ctrl) { ev.Use(); NGUITools.clipboard = GetSelection(); } return true; } // Paste case KeyCode.V: { if (ctrl) { ev.Use(); Insert(NGUITools.clipboard); } return true; } // Cut case KeyCode.X: { if (ctrl) { ev.Use(); NGUITools.clipboard = GetSelection(); Insert(""); } return true; } } return false; } #endif /// /// Insert the specified text string into the current input value, respecting selection and validation. /// protected virtual void Insert (string text) { string left = GetLeftText(); string right = GetRightText(); int rl = right.Length; StringBuilder sb = new StringBuilder(left.Length + right.Length + text.Length); sb.Append(left); // Append the new text for (int i = 0, imax = text.Length; i < imax; ++i) { // If we have an input validator, validate the input first char c = text[i]; if (c == '\b') { DoBackspace(); continue; } // Can't go past the character limit if (characterLimit > 0 && sb.Length + rl >= characterLimit) break; if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c); else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c); // Append the character if it hasn't been invalidated if (c != 0) sb.Append(c); } // Advance the selection mSelectionStart = sb.Length; mSelectionEnd = mSelectionStart; // Append the text that follows it, ensuring that it's also validated after the inserted value for (int i = 0, imax = right.Length; i < imax; ++i) { char c = right[i]; if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c); else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c); if (c != 0) sb.Append(c); } mValue = sb.ToString(); UpdateLabel(); ExecuteOnChange(); } /// /// Get the text to the left of the selection. /// protected string GetLeftText () { int min = Mathf.Min(mSelectionStart, mSelectionEnd); return (string.IsNullOrEmpty(mValue) || min < 0) ? "" : mValue.Substring(0, min); } /// /// Get the text to the right of the selection. /// protected string GetRightText () { int max = Mathf.Max(mSelectionStart, mSelectionEnd); return (string.IsNullOrEmpty(mValue) || max >= mValue.Length) ? "" : mValue.Substring(max); } /// /// Get currently selected text. /// protected string GetSelection () { if (string.IsNullOrEmpty(mValue) || mSelectionStart == mSelectionEnd) { return ""; } else { int min = Mathf.Min(mSelectionStart, mSelectionEnd); int max = Mathf.Max(mSelectionStart, mSelectionEnd); return mValue.Substring(min, max - min); } } /// /// Helper function that retrieves the index of the character under the mouse. /// protected int GetCharUnderMouse () { Vector3[] corners = label.worldCorners; Ray ray = UICamera.currentRay; Plane p = new Plane(corners[0], corners[1], corners[2]); float dist; return p.Raycast(ray, out dist) ? mDrawStart + label.GetCharacterIndexAtPosition(ray.GetPoint(dist), false) : 0; } /// /// Move the caret on press. /// protected virtual void OnPress (bool isPressed) { if (isPressed && isSelected && label != null && (UICamera.currentScheme == UICamera.ControlScheme.Mouse || UICamera.currentScheme == UICamera.ControlScheme.Touch)) { #if !UNITY_EDITOR && (UNITY_WP8 || UNITY_WP_8_1) if (mKeyboard != null) mKeyboard.active = true; #endif selectionEnd = GetCharUnderMouse(); if (!Input.GetKey(KeyCode.LeftShift) && !Input.GetKey(KeyCode.RightShift)) selectionStart = mSelectionEnd; } } /// /// Drag selection. /// protected virtual void OnDrag (Vector2 delta) { if (label != null && (UICamera.currentScheme == UICamera.ControlScheme.Mouse || UICamera.currentScheme == UICamera.ControlScheme.Touch)) { selectionEnd = GetCharUnderMouse(); } } /// /// Ensure we've released the dynamically created resources. /// void OnDisable () { Cleanup(); } /// /// Cleanup. /// protected virtual void Cleanup () { if (mHighlight) mHighlight.enabled = false; if (mCaret) mCaret.enabled = false; if (mBlankTex) { NGUITools.Destroy(mBlankTex); mBlankTex = null; } } /// /// Submit the input field's text. /// public void Submit () { if (NGUITools.GetActive(this)) { mValue = value; if (current == null) { current = this; EventDelegate.Execute(onSubmit); current = null; } SaveToPlayerPrefs(mValue); } } /// /// Update the visual text label. /// public void UpdateLabel () { if (label != null) { if (mDoInit) Init(); bool selected = isSelected; string fullText = value; bool isEmpty = string.IsNullOrEmpty(fullText) && string.IsNullOrEmpty(Input.compositionString); label.color = (isEmpty && !selected) ? mDefaultColor : activeTextColor; string processed; if (isEmpty) { processed = selected ? "" : mDefaultText; label.alignment = mAlignment; } else { if (inputType == InputType.Password) { processed = ""; string asterisk = "*"; if (label.bitmapFont != null && label.bitmapFont.bmFont != null && label.bitmapFont.bmFont.GetGlyph('*') == null) asterisk = "x"; for (int i = 0, imax = fullText.Length; i < imax; ++i) processed += asterisk; } else processed = fullText; // Start with text leading up to the selection int selPos = selected ? Mathf.Min(processed.Length, cursorPosition) : 0; string left = processed.Substring(0, selPos); // Append the composition string and the cursor character if (selected) left += Input.compositionString; // Append the text from the selection onwards processed = left + processed.Substring(selPos, processed.Length - selPos); // Clamped content needs to be adjusted further if (selected && label.overflowMethod == UILabel.Overflow.ClampContent && label.maxLineCount == 1) { // Determine what will actually fit into the given line int offset = label.CalculateOffsetToFit(processed); if (offset == 0) { mDrawStart = 0; label.alignment = mAlignment; } else if (selPos < mDrawStart) { mDrawStart = selPos; label.alignment = NGUIText.Alignment.Left; } else if (offset < mDrawStart) { mDrawStart = offset; label.alignment = NGUIText.Alignment.Left; } else { offset = label.CalculateOffsetToFit(processed.Substring(0, selPos)); if (offset > mDrawStart) { mDrawStart = offset; label.alignment = NGUIText.Alignment.Right; } } // If necessary, trim the front if (mDrawStart != 0) processed = processed.Substring(mDrawStart, processed.Length - mDrawStart); } else { mDrawStart = 0; label.alignment = mAlignment; } } label.text = processed; #if MOBILE if (selected && (mKeyboard == null || inputShouldBeHidden)) #else if (selected) #endif { int start = mSelectionStart - mDrawStart; int end = mSelectionEnd - mDrawStart; // Blank texture used by selection and caret if (mBlankTex == null) { mBlankTex = new Texture2D(2, 2, TextureFormat.ARGB32, false); for (int y = 0; y < 2; ++y) for (int x = 0; x < 2; ++x) mBlankTex.SetPixel(x, y, Color.white); mBlankTex.Apply(); } // Create the selection highlight if (start != end) { if (mHighlight == null) { mHighlight = NGUITools.AddWidget(label.cachedGameObject); mHighlight.name = "Input Highlight"; mHighlight.mainTexture = mBlankTex; mHighlight.fillGeometry = false; mHighlight.pivot = label.pivot; mHighlight.SetAnchor(label.cachedTransform); } else { mHighlight.pivot = label.pivot; mHighlight.mainTexture = mBlankTex; mHighlight.MarkAsChanged(); mHighlight.enabled = true; } } // Create the carter if (mCaret == null) { mCaret = NGUITools.AddWidget(label.cachedGameObject); mCaret.name = "Input Caret"; mCaret.mainTexture = mBlankTex; mCaret.fillGeometry = false; mCaret.pivot = label.pivot; mCaret.SetAnchor(label.cachedTransform); } else { mCaret.pivot = label.pivot; mCaret.mainTexture = mBlankTex; mCaret.MarkAsChanged(); mCaret.enabled = true; } if (start != end) { label.PrintOverlay(start, end, mCaret.geometry, mHighlight.geometry, caretColor, selectionColor); mHighlight.enabled = mHighlight.geometry.hasVertices; } else { label.PrintOverlay(start, end, mCaret.geometry, null, caretColor, selectionColor); if (mHighlight != null) mHighlight.enabled = false; } // Reset the blinking time mNextBlink = RealTime.time + 0.5f; mLastAlpha = label.finalAlpha; } else Cleanup(); } } /// /// Validate the specified input. /// protected char Validate (string text, int pos, char ch) { // Validation is disabled if (validation == Validation.None || !enabled) return ch; if (validation == Validation.Integer) { // Integer number validation if (ch >= '0' && ch <= '9') return ch; if (ch == '-' && pos == 0 && !text.Contains("-")) return ch; } else if (validation == Validation.Float) { // Floating-point number if (ch >= '0' && ch <= '9') return ch; if (ch == '-' && pos == 0 && !text.Contains("-")) return ch; if (ch == '.' && !text.Contains(".")) return ch; } else if (validation == Validation.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 (validation == Validation.Username) { // Lowercase and numbers if (ch >= 'A' && ch <= 'Z') return (char)(ch - 'A' + 'a'); if (ch >= 'a' && ch <= 'z') return ch; if (ch >= '0' && ch <= '9') return ch; } else if (validation == Validation.Filename) { if (ch == ':') return (char)0; if (ch == '/') return (char)0; if (ch == '\\') return (char)0; if (ch == '<') return (char)0; if (ch == '>') return (char)0; if (ch == '|') return (char)0; if (ch == '^') return (char)0; if (ch == '*') return (char)0; if (ch == ';') return (char)0; if (ch == '"') return (char)0; if (ch == '`') return (char)0; if (ch == '\t') return (char)0; if (ch == '\n') return (char)0; return ch; } else if (validation == Validation.Name) { 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 (ch >= 'a' && ch <= 'z') { // Space followed by a letter -- make sure it's capitalized if (lastChar == ' ') return (char)(ch - 'a' + 'A'); return ch; } else if (ch >= 'A' && ch <= 'Z') { // Uppercase letters are only allowed after spaces (and apostrophes) if (lastChar != ' ' && lastChar != '\'') return (char)(ch - 'A' + 'a'); return ch; } else if (ch == '\'') { // Don't allow more than one apostrophe if (lastChar != ' ' && lastChar != '\'' && nextChar != '\'' && !text.Contains("'")) return ch; } else if (ch == ' ') { // Don't allow more than one space in a row if (lastChar != ' ' && lastChar != '\'' && nextChar != ' ' && nextChar != '\'') return ch; } } return (char)0; } /// /// Execute the OnChange callback. /// protected void ExecuteOnChange () { if (current == null && EventDelegate.IsValid(onChange)) { current = this; EventDelegate.Execute(onChange); current = null; } } /// /// Convenience function to be used as a callback that will clear the input field's focus. /// public void RemoveFocus () { isSelected = false; } /// /// Convenience function that can be used as a callback for On Change notification. /// public void SaveValue () { SaveToPlayerPrefs(mValue); } /// /// Convenience function that can forcefully reset the input field's value to what was saved earlier. /// public void LoadValue () { if (!string.IsNullOrEmpty(savedAs)) { string val = mValue.Replace("\\n", "\n"); mValue = ""; value = PlayerPrefs.HasKey(savedAs) ? PlayerPrefs.GetString(savedAs) : val; } } }