PageRenderTime 73ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/Demo_NGUI_3.5/Assets/NGUI/Scripts/UI/UIInput.cs

https://github.com/dandanshih/UnityUI
C# | 1216 lines | 849 code | 184 blank | 183 comment | 312 complexity | e05aaadf84be35cd0ebff863e02244d5 MD5 | raw file
  1. //----------------------------------------------
  2. // NGUI: Next-Gen UI kit
  3. // Copyright Š 2011-2014 Tasharen Entertainment
  4. //----------------------------------------------
  5. #if !UNITY_EDITOR && (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_BLACKBERRY)
  6. #define MOBILE
  7. #endif
  8. using UnityEngine;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. /// <summary>
  12. /// Input field makes it possible to enter custom information within the UI.
  13. /// </summary>
  14. [AddComponentMenu("NGUI/UI/Input Field")]
  15. public class UIInput : MonoBehaviour
  16. {
  17. public enum InputType
  18. {
  19. Standard,
  20. AutoCorrect,
  21. Password,
  22. }
  23. public enum Validation
  24. {
  25. None,
  26. Integer,
  27. Float,
  28. Alphanumeric,
  29. Username,
  30. Name,
  31. }
  32. public enum KeyboardType
  33. {
  34. Default = 0,
  35. ASCIICapable = 1,
  36. NumbersAndPunctuation = 2,
  37. URL = 3,
  38. NumberPad = 4,
  39. PhonePad = 5,
  40. NamePhonePad = 6,
  41. EmailAddress = 7,
  42. }
  43. public delegate char OnValidate (string text, int charIndex, char addedChar);
  44. /// <summary>
  45. /// Currently active input field. Only valid during callbacks.
  46. /// </summary>
  47. static public UIInput current;
  48. /// <summary>
  49. /// Currently selected input field, if any.
  50. /// </summary>
  51. static public UIInput selection;
  52. /// <summary>
  53. /// Text label used to display the input's value.
  54. /// </summary>
  55. public UILabel label;
  56. /// <summary>
  57. /// Type of data expected by the input field.
  58. /// </summary>
  59. public InputType inputType = InputType.Standard;
  60. /// <summary>
  61. /// Keyboard type applies to mobile keyboards that get shown.
  62. /// </summary>
  63. public KeyboardType keyboardType = KeyboardType.Default;
  64. /// <summary>
  65. /// What kind of validation to use with the input field's data.
  66. /// </summary>
  67. public Validation validation = Validation.None;
  68. /// <summary>
  69. /// Maximum number of characters allowed before input no longer works.
  70. /// </summary>
  71. public int characterLimit = 0;
  72. /// <summary>
  73. /// Field in player prefs used to automatically save the value.
  74. /// </summary>
  75. public string savedAs;
  76. /// <summary>
  77. /// Object to select when Tab key gets pressed.
  78. /// </summary>
  79. public GameObject selectOnTab;
  80. /// <summary>
  81. /// Color of the label when the input field has focus.
  82. /// </summary>
  83. public Color activeTextColor = Color.white;
  84. /// <summary>
  85. /// Color used by the caret symbol.
  86. /// </summary>
  87. public Color caretColor = new Color(1f, 1f, 1f, 0.8f);
  88. /// <summary>
  89. /// Color used by the selection rectangle.
  90. /// </summary>
  91. public Color selectionColor = new Color(1f, 223f / 255f, 141f / 255f, 0.5f);
  92. /// <summary>
  93. /// Event delegates triggered when the input field submits its data.
  94. /// </summary>
  95. public List<EventDelegate> onSubmit = new List<EventDelegate>();
  96. /// <summary>
  97. /// Event delegates triggered when the input field's text changes for any reason.
  98. /// </summary>
  99. public List<EventDelegate> onChange = new List<EventDelegate>();
  100. /// <summary>
  101. /// Custom validation callback.
  102. /// </summary>
  103. public OnValidate onValidate;
  104. /// <summary>
  105. /// Input field's value.
  106. /// </summary>
  107. [SerializeField][HideInInspector] protected string mValue;
  108. protected string mDefaultText = "";
  109. protected Color mDefaultColor = Color.white;
  110. protected float mPosition = 0f;
  111. protected bool mDoInit = true;
  112. protected UIWidget.Pivot mPivot = UIWidget.Pivot.TopLeft;
  113. static protected int mDrawStart = 0;
  114. #if MOBILE
  115. static protected TouchScreenKeyboard mKeyboard;
  116. #else
  117. protected int mSelectionStart = 0;
  118. protected int mSelectionEnd = 0;
  119. protected UITexture mHighlight = null;
  120. protected UITexture mCaret = null;
  121. protected Texture2D mBlankTex = null;
  122. protected float mNextBlink = 0f;
  123. static protected string mLastIME = "";
  124. #endif
  125. /// <summary>
  126. /// Default text used by the input's label.
  127. /// </summary>
  128. public string defaultText
  129. {
  130. get
  131. {
  132. return mDefaultText;
  133. }
  134. set
  135. {
  136. if (mDoInit) Init();
  137. mDefaultText = value;
  138. UpdateLabel();
  139. }
  140. }
  141. [System.Obsolete("Use UIInput.value instead")]
  142. public string text { get { return this.value; } set { this.value = value; } }
  143. /// <summary>
  144. /// Input field's current text value.
  145. /// </summary>
  146. public string value
  147. {
  148. get
  149. {
  150. #if UNITY_EDITOR
  151. if (!Application.isPlaying) return "";
  152. #endif
  153. if (mDoInit) Init();
  154. return mValue;
  155. }
  156. set
  157. {
  158. #if UNITY_EDITOR
  159. if (!Application.isPlaying) return;
  160. #endif
  161. if (mDoInit) Init();
  162. mDrawStart = 0;
  163. #if MOBILE && !UNITY_3_5
  164. // BB10's implementation has a bug in Unity
  165. if (Application.platform == RuntimePlatform.BB10Player)
  166. value = value.Replace("\\b", "\b");
  167. #endif
  168. // Validate all input
  169. value = Validate(value);
  170. #if MOBILE
  171. if (isSelected && mKeyboard != null && mCached != value)
  172. {
  173. mKeyboard.text = value;
  174. mCached = value;
  175. }
  176. if (mValue != value)
  177. {
  178. mValue = value;
  179. if (!isSelected) SaveToPlayerPrefs(value);
  180. UpdateLabel();
  181. ExecuteOnChange();
  182. }
  183. #else
  184. if (mValue != value)
  185. {
  186. mValue = value;
  187. if (isSelected)
  188. {
  189. if (string.IsNullOrEmpty(value))
  190. {
  191. mSelectionStart = 0;
  192. mSelectionEnd = 0;
  193. }
  194. else
  195. {
  196. mSelectionStart = value.Length;
  197. mSelectionEnd = mSelectionStart;
  198. }
  199. }
  200. else SaveToPlayerPrefs(value);
  201. UpdateLabel();
  202. ExecuteOnChange();
  203. }
  204. #endif
  205. }
  206. }
  207. [System.Obsolete("Use UIInput.isSelected instead")]
  208. public bool selected { get { return isSelected; } set { isSelected = value; } }
  209. /// <summary>
  210. /// Whether the input is currently selected.
  211. /// </summary>
  212. public bool isSelected
  213. {
  214. get
  215. {
  216. return selection == this;
  217. }
  218. set
  219. {
  220. if (!value) { if (isSelected) UICamera.selectedObject = null; }
  221. else UICamera.selectedObject = gameObject;
  222. }
  223. }
  224. /// <summary>
  225. /// Current position of the cursor.
  226. /// </summary>
  227. #if MOBILE
  228. protected int cursorPosition { get { return value.Length; } }
  229. #else
  230. protected int cursorPosition { get { return isSelected ? mSelectionEnd : value.Length; } }
  231. #endif
  232. /// <summary>
  233. /// Validate the specified text, returning the validated version.
  234. /// </summary>
  235. public string Validate (string val)
  236. {
  237. if (string.IsNullOrEmpty(val)) return "";
  238. StringBuilder sb = new StringBuilder(val.Length);
  239. for (int i = 0; i < val.Length; ++i)
  240. {
  241. char c = val[i];
  242. if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
  243. else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
  244. if (c != 0) sb.Append(c);
  245. }
  246. if (characterLimit > 0 && sb.Length > characterLimit)
  247. return sb.ToString(0, characterLimit);
  248. return sb.ToString();
  249. }
  250. /// <summary>
  251. /// Automatically set the value by loading it from player prefs if possible.
  252. /// </summary>
  253. void Start ()
  254. {
  255. if (string.IsNullOrEmpty(mValue))
  256. {
  257. if (!string.IsNullOrEmpty(savedAs) && PlayerPrefs.HasKey(savedAs))
  258. value = PlayerPrefs.GetString(savedAs);
  259. }
  260. else value = mValue.Replace("\\n", "\n");
  261. }
  262. /// <summary>
  263. /// Labels used for input shouldn't support rich text.
  264. /// </summary>
  265. protected void Init ()
  266. {
  267. if (mDoInit && label != null)
  268. {
  269. mDoInit = false;
  270. mDefaultText = label.text;
  271. mDefaultColor = label.color;
  272. label.supportEncoding = false;
  273. if (label.alignment == NGUIText.Alignment.Justified)
  274. {
  275. label.alignment = NGUIText.Alignment.Left;
  276. Debug.LogWarning("Input fields using labels with justified alignment are not supported at this time", this);
  277. }
  278. mPivot = label.pivot;
  279. mPosition = label.cachedTransform.localPosition.x;
  280. UpdateLabel();
  281. }
  282. }
  283. /// <summary>
  284. /// Save the specified value to player prefs.
  285. /// </summary>
  286. protected void SaveToPlayerPrefs (string val)
  287. {
  288. if (!string.IsNullOrEmpty(savedAs))
  289. {
  290. if (string.IsNullOrEmpty(val)) PlayerPrefs.DeleteKey(savedAs);
  291. else PlayerPrefs.SetString(savedAs, val);
  292. }
  293. }
  294. /// <summary>
  295. /// Selection event, sent by the EventSystem.
  296. /// </summary>
  297. protected virtual void OnSelect (bool isSelected)
  298. {
  299. if (isSelected) OnSelectEvent();
  300. else OnDeselectEvent();
  301. }
  302. /// <summary>
  303. /// Notification of the input field gaining selection.
  304. /// </summary>
  305. protected void OnSelectEvent ()
  306. {
  307. selection = this;
  308. if (mDoInit) Init();
  309. if (label != null && NGUITools.GetActive(this))
  310. {
  311. label.color = activeTextColor;
  312. #if MOBILE
  313. if (Application.platform == RuntimePlatform.IPhonePlayer ||
  314. Application.platform == RuntimePlatform.Android
  315. #if UNITY_WP8
  316. || Application.platform == RuntimePlatform.WP8Player
  317. #endif
  318. #if UNITY_BLACKBERRY
  319. || Application.platform == RuntimePlatform.BB10Player
  320. #endif
  321. )
  322. {
  323. mKeyboard = (inputType == InputType.Password) ?
  324. TouchScreenKeyboard.Open(mValue, TouchScreenKeyboardType.Default, false, false, true) :
  325. TouchScreenKeyboard.Open(mValue, (TouchScreenKeyboardType)((int)keyboardType), inputType == InputType.AutoCorrect, label.multiLine, false, false, defaultText);
  326. }
  327. else
  328. #endif
  329. {
  330. Vector2 pos = (UICamera.current != null && UICamera.current.cachedCamera != null) ?
  331. UICamera.current.cachedCamera.WorldToScreenPoint(label.worldCorners[0]) :
  332. label.worldCorners[0];
  333. pos.y = Screen.height - pos.y;
  334. Input.imeCompositionMode = IMECompositionMode.On;
  335. Input.compositionCursorPos = pos;
  336. #if !MOBILE
  337. mSelectionStart = 0;
  338. mSelectionEnd = string.IsNullOrEmpty(mValue) ? 0 : mValue.Length;
  339. #endif
  340. mDrawStart = 0;
  341. }
  342. UpdateLabel();
  343. }
  344. }
  345. /// <summary>
  346. /// Notification of the input field losing selection.
  347. /// </summary>
  348. protected void OnDeselectEvent ()
  349. {
  350. if (mDoInit) Init();
  351. if (label != null && NGUITools.GetActive(this))
  352. {
  353. mValue = value;
  354. #if MOBILE
  355. if (mKeyboard != null)
  356. {
  357. mKeyboard.active = false;
  358. mKeyboard = null;
  359. }
  360. #endif
  361. if (string.IsNullOrEmpty(mValue))
  362. {
  363. label.text = mDefaultText;
  364. label.color = mDefaultColor;
  365. }
  366. else label.text = mValue;
  367. Input.imeCompositionMode = IMECompositionMode.Auto;
  368. RestoreLabelPivot();
  369. }
  370. selection = null;
  371. UpdateLabel();
  372. }
  373. /// <summary>
  374. /// Update the text based on input.
  375. /// </summary>
  376. #if MOBILE
  377. string mCached = "";
  378. void Update()
  379. {
  380. if (mKeyboard != null && isSelected)
  381. {
  382. string text = mKeyboard.text;
  383. if (mCached != text)
  384. {
  385. mCached = text;
  386. value = text;
  387. }
  388. if (mKeyboard.done)
  389. {
  390. #if !UNITY_3_5
  391. if (!mKeyboard.wasCanceled)
  392. #endif
  393. Submit();
  394. mKeyboard = null;
  395. isSelected = false;
  396. mCached = "";
  397. }
  398. }
  399. }
  400. #else
  401. void Update ()
  402. {
  403. #if UNITY_EDITOR
  404. if (!Application.isPlaying) return;
  405. #endif
  406. if (isSelected)
  407. {
  408. if (mDoInit) Init();
  409. if (selectOnTab != null && Input.GetKeyDown(KeyCode.Tab))
  410. {
  411. UICamera.selectedObject = selectOnTab;
  412. return;
  413. }
  414. string ime = Input.compositionString;
  415. // There seems to be an inconsistency between IME on Windows, and IME on OSX.
  416. // On Windows, Input.inputString is always empty while IME is active. On the OSX it is not.
  417. if (string.IsNullOrEmpty(ime) && !string.IsNullOrEmpty(Input.inputString))
  418. {
  419. // Process input ignoring non-printable characters as they are not consistent.
  420. // Windows has them, OSX may not. They get handled inside OnGUI() instead.
  421. string s = Input.inputString;
  422. for (int i = 0; i < s.Length; ++i)
  423. {
  424. char ch = s[i];
  425. if (ch >= ' ') Insert(ch.ToString());
  426. }
  427. }
  428. // Append IME composition
  429. if (mLastIME != ime)
  430. {
  431. mSelectionEnd = string.IsNullOrEmpty(ime) ? mSelectionStart : mValue.Length + ime.Length;
  432. mLastIME = ime;
  433. UpdateLabel();
  434. ExecuteOnChange();
  435. }
  436. // Blink the caret
  437. if (mCaret != null && mNextBlink < RealTime.time)
  438. {
  439. mNextBlink = RealTime.time + 0.5f;
  440. mCaret.enabled = !mCaret.enabled;
  441. }
  442. }
  443. }
  444. /// <summary>
  445. /// Unfortunately Unity 4.3 and earlier doesn't offer a way to properly process events outside of OnGUI.
  446. /// </summary>
  447. void OnGUI ()
  448. {
  449. if (isSelected && Event.current.rawType == EventType.KeyDown)
  450. ProcessEvent(Event.current);
  451. }
  452. /// <summary>
  453. /// Handle the specified event.
  454. /// </summary>
  455. bool ProcessEvent (Event ev)
  456. {
  457. if (label == null) return false;
  458. RuntimePlatform rp = Application.platform;
  459. bool isMac = (
  460. rp == RuntimePlatform.OSXEditor ||
  461. rp == RuntimePlatform.OSXPlayer ||
  462. rp == RuntimePlatform.OSXWebPlayer);
  463. bool ctrl = isMac ?
  464. ((ev.modifiers & EventModifiers.Command) != 0) :
  465. ((ev.modifiers & EventModifiers.Control) != 0);
  466. bool shift = ((ev.modifiers & EventModifiers.Shift) != 0);
  467. switch (ev.keyCode)
  468. {
  469. case KeyCode.Backspace:
  470. {
  471. ev.Use();
  472. if (!string.IsNullOrEmpty(mValue))
  473. {
  474. if (mSelectionStart == mSelectionEnd)
  475. {
  476. if (mSelectionStart < 1) return true;
  477. --mSelectionEnd;
  478. }
  479. Insert("");
  480. }
  481. return true;
  482. }
  483. case KeyCode.Delete:
  484. {
  485. ev.Use();
  486. if (!string.IsNullOrEmpty(mValue))
  487. {
  488. if (mSelectionStart == mSelectionEnd)
  489. {
  490. if (mSelectionStart >= mValue.Length) return true;
  491. ++mSelectionEnd;
  492. }
  493. Insert("");
  494. }
  495. return true;
  496. }
  497. case KeyCode.LeftArrow:
  498. {
  499. ev.Use();
  500. if (!string.IsNullOrEmpty(mValue))
  501. {
  502. mSelectionEnd = Mathf.Max(mSelectionEnd - 1, 0);
  503. if (!shift) mSelectionStart = mSelectionEnd;
  504. UpdateLabel();
  505. }
  506. return true;
  507. }
  508. case KeyCode.RightArrow:
  509. {
  510. ev.Use();
  511. if (!string.IsNullOrEmpty(mValue))
  512. {
  513. mSelectionEnd = Mathf.Min(mSelectionEnd + 1, mValue.Length);
  514. if (!shift) mSelectionStart = mSelectionEnd;
  515. UpdateLabel();
  516. }
  517. return true;
  518. }
  519. case KeyCode.PageUp:
  520. {
  521. ev.Use();
  522. if (!string.IsNullOrEmpty(mValue))
  523. {
  524. mSelectionEnd = 0;
  525. if (!shift) mSelectionStart = mSelectionEnd;
  526. UpdateLabel();
  527. }
  528. return true;
  529. }
  530. case KeyCode.PageDown:
  531. {
  532. ev.Use();
  533. if (!string.IsNullOrEmpty(mValue))
  534. {
  535. mSelectionEnd = mValue.Length;
  536. if (!shift) mSelectionStart = mSelectionEnd;
  537. UpdateLabel();
  538. }
  539. return true;
  540. }
  541. case KeyCode.Home:
  542. {
  543. ev.Use();
  544. if (!string.IsNullOrEmpty(mValue))
  545. {
  546. if (label.multiLine)
  547. {
  548. mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.Home);
  549. }
  550. else mSelectionEnd = 0;
  551. if (!shift) mSelectionStart = mSelectionEnd;
  552. UpdateLabel();
  553. }
  554. return true;
  555. }
  556. case KeyCode.End:
  557. {
  558. ev.Use();
  559. if (!string.IsNullOrEmpty(mValue))
  560. {
  561. if (label.multiLine)
  562. {
  563. mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.End);
  564. }
  565. else mSelectionEnd = mValue.Length;
  566. if (!shift) mSelectionStart = mSelectionEnd;
  567. UpdateLabel();
  568. }
  569. return true;
  570. }
  571. case KeyCode.UpArrow:
  572. {
  573. ev.Use();
  574. if (!string.IsNullOrEmpty(mValue))
  575. {
  576. mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.UpArrow);
  577. if (mSelectionEnd != 0) mSelectionEnd += mDrawStart;
  578. if (!shift) mSelectionStart = mSelectionEnd;
  579. UpdateLabel();
  580. }
  581. return true;
  582. }
  583. case KeyCode.DownArrow:
  584. {
  585. ev.Use();
  586. if (!string.IsNullOrEmpty(mValue))
  587. {
  588. mSelectionEnd = label.GetCharacterIndex(mSelectionEnd, KeyCode.DownArrow);
  589. if (mSelectionEnd != label.processedText.Length) mSelectionEnd += mDrawStart;
  590. else mSelectionEnd = mValue.Length;
  591. if (!shift) mSelectionStart = mSelectionEnd;
  592. UpdateLabel();
  593. }
  594. return true;
  595. }
  596. // Copy
  597. case KeyCode.C:
  598. {
  599. if (ctrl)
  600. {
  601. ev.Use();
  602. NGUITools.clipboard = GetSelection();
  603. }
  604. return true;
  605. }
  606. // Paste
  607. case KeyCode.V:
  608. {
  609. if (ctrl)
  610. {
  611. ev.Use();
  612. Insert(NGUITools.clipboard);
  613. }
  614. return true;
  615. }
  616. // Cut
  617. case KeyCode.X:
  618. {
  619. if (ctrl)
  620. {
  621. ev.Use();
  622. NGUITools.clipboard = GetSelection();
  623. Insert("");
  624. }
  625. return true;
  626. }
  627. // Submit
  628. case KeyCode.Return:
  629. case KeyCode.KeypadEnter:
  630. {
  631. ev.Use();
  632. if (label.multiLine && !ctrl && label.overflowMethod != UILabel.Overflow.ClampContent)
  633. {
  634. Insert("\n");
  635. }
  636. else
  637. {
  638. UICamera.currentScheme = UICamera.ControlScheme.Controller;
  639. UICamera.currentKey = ev.keyCode;
  640. Submit();
  641. UICamera.currentKey = KeyCode.None;
  642. }
  643. return true;
  644. }
  645. }
  646. return false;
  647. }
  648. /// <summary>
  649. /// Insert the specified text string into the current input value, respecting selection and validation.
  650. /// </summary>
  651. protected virtual void Insert (string text)
  652. {
  653. string left = GetLeftText();
  654. string right = GetRightText();
  655. int rl = right.Length;
  656. StringBuilder sb = new StringBuilder(left.Length + right.Length + text.Length);
  657. sb.Append(left);
  658. // Append the new text
  659. for (int i = 0, imax = text.Length; i < imax; ++i)
  660. {
  661. // Can't go past the character limit
  662. if (characterLimit > 0 && sb.Length + rl >= characterLimit) break;
  663. // If we have an input validator, validate the input first
  664. char c = text[i];
  665. if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
  666. else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
  667. // Append the character if it hasn't been invalidated
  668. if (c != 0) sb.Append(c);
  669. }
  670. // Advance the selection
  671. mSelectionStart = sb.Length;
  672. mSelectionEnd = mSelectionStart;
  673. // Append the text that follows it, ensuring that it's also validated after the inserted value
  674. for (int i = 0, imax = right.Length; i < imax; ++i)
  675. {
  676. char c = right[i];
  677. if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
  678. else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
  679. if (c != 0) sb.Append(c);
  680. }
  681. mValue = sb.ToString();
  682. UpdateLabel();
  683. ExecuteOnChange();
  684. }
  685. /// <summary>
  686. /// Get the text to the left of the selection.
  687. /// </summary>
  688. protected string GetLeftText ()
  689. {
  690. int min = Mathf.Min(mSelectionStart, mSelectionEnd);
  691. return (string.IsNullOrEmpty(mValue) || min < 0) ? "" : mValue.Substring(0, min);
  692. }
  693. /// <summary>
  694. /// Get the text to the right of the selection.
  695. /// </summary>
  696. protected string GetRightText ()
  697. {
  698. int max = Mathf.Max(mSelectionStart, mSelectionEnd);
  699. return (string.IsNullOrEmpty(mValue) || max >= mValue.Length) ? "" : mValue.Substring(max);
  700. }
  701. /// <summary>
  702. /// Get currently selected text.
  703. /// </summary>
  704. protected string GetSelection ()
  705. {
  706. if (string.IsNullOrEmpty(mValue) || mSelectionStart == mSelectionEnd)
  707. {
  708. return "";
  709. }
  710. else
  711. {
  712. int min = Mathf.Min(mSelectionStart, mSelectionEnd);
  713. int max = Mathf.Max(mSelectionStart, mSelectionEnd);
  714. return mValue.Substring(min, max - min);
  715. }
  716. }
  717. /// <summary>
  718. /// Helper function that retrieves the index of the character under the mouse.
  719. /// </summary>
  720. protected int GetCharUnderMouse ()
  721. {
  722. Vector3[] corners = label.worldCorners;
  723. Ray ray = UICamera.currentRay;
  724. Plane p = new Plane(corners[0], corners[1], corners[2]);
  725. float dist;
  726. return p.Raycast(ray, out dist) ? mDrawStart + label.GetCharacterIndexAtPosition(ray.GetPoint(dist)) : 0;
  727. }
  728. /// <summary>
  729. /// Move the caret on press.
  730. /// </summary>
  731. protected virtual void OnPress (bool isPressed)
  732. {
  733. if (isPressed && isSelected && label != null && UICamera.currentScheme == UICamera.ControlScheme.Mouse)
  734. {
  735. mSelectionEnd = GetCharUnderMouse();
  736. if (!Input.GetKey(KeyCode.LeftShift) &&
  737. !Input.GetKey(KeyCode.RightShift)) mSelectionStart = mSelectionEnd;
  738. UpdateLabel();
  739. }
  740. }
  741. /// <summary>
  742. /// Drag selection.
  743. /// </summary>
  744. protected virtual void OnDrag (Vector2 delta)
  745. {
  746. if (label != null && UICamera.currentScheme == UICamera.ControlScheme.Mouse)
  747. {
  748. mSelectionEnd = GetCharUnderMouse();
  749. UpdateLabel();
  750. }
  751. }
  752. /// <summary>
  753. /// Ensure we've released the dynamically created resources.
  754. /// </summary>
  755. void OnDisable () { Cleanup(); }
  756. /// <summary>
  757. /// Cleanup.
  758. /// </summary>
  759. protected virtual void Cleanup ()
  760. {
  761. if (mHighlight)
  762. {
  763. NGUITools.Destroy(mHighlight.gameObject);
  764. mHighlight = null;
  765. }
  766. if (mCaret)
  767. {
  768. NGUITools.Destroy(mCaret.gameObject);
  769. mCaret = null;
  770. }
  771. if (mBlankTex)
  772. {
  773. NGUITools.Destroy(mBlankTex);
  774. mBlankTex = null;
  775. }
  776. }
  777. #endif // !MOBILE
  778. /// <summary>
  779. /// Submit the input field's text.
  780. /// </summary>
  781. public void Submit ()
  782. {
  783. if (NGUITools.GetActive(this))
  784. {
  785. current = this;
  786. mValue = value;
  787. EventDelegate.Execute(onSubmit);
  788. SaveToPlayerPrefs(mValue);
  789. current = null;
  790. }
  791. }
  792. /// <summary>
  793. /// Update the visual text label.
  794. /// </summary>
  795. public void UpdateLabel ()
  796. {
  797. if (label != null)
  798. {
  799. if (mDoInit) Init();
  800. bool selected = isSelected;
  801. string fullText = value;
  802. bool isEmpty = string.IsNullOrEmpty(fullText) && string.IsNullOrEmpty(Input.compositionString);
  803. label.color = (isEmpty && !selected) ? mDefaultColor : activeTextColor;
  804. string processed;
  805. if (isEmpty)
  806. {
  807. processed = selected ? "" : mDefaultText;
  808. RestoreLabelPivot();
  809. }
  810. else
  811. {
  812. if (inputType == InputType.Password)
  813. {
  814. processed = "";
  815. for (int i = 0, imax = fullText.Length; i < imax; ++i) processed += "*";
  816. }
  817. else processed = fullText;
  818. // Start with text leading up to the selection
  819. int selPos = selected ? Mathf.Min(processed.Length, cursorPosition) : 0;
  820. string left = processed.Substring(0, selPos);
  821. // Append the composition string and the cursor character
  822. if (selected) left += Input.compositionString;
  823. // Append the text from the selection onwards
  824. processed = left + processed.Substring(selPos, processed.Length - selPos);
  825. // Clamped content needs to be adjusted further
  826. if (selected && label.overflowMethod == UILabel.Overflow.ClampContent)
  827. {
  828. // Determine what will actually fit into the given line
  829. int offset = label.CalculateOffsetToFit(processed);
  830. if (offset == 0)
  831. {
  832. mDrawStart = 0;
  833. RestoreLabelPivot();
  834. }
  835. else if (selPos < mDrawStart)
  836. {
  837. mDrawStart = selPos;
  838. SetPivotToLeft();
  839. }
  840. else if (offset < mDrawStart)
  841. {
  842. mDrawStart = offset;
  843. SetPivotToLeft();
  844. }
  845. else
  846. {
  847. offset = label.CalculateOffsetToFit(processed.Substring(0, selPos));
  848. if (offset > mDrawStart)
  849. {
  850. mDrawStart = offset;
  851. SetPivotToRight();
  852. }
  853. }
  854. // If necessary, trim the front
  855. if (mDrawStart != 0)
  856. processed = processed.Substring(mDrawStart, processed.Length - mDrawStart);
  857. }
  858. else
  859. {
  860. mDrawStart = 0;
  861. RestoreLabelPivot();
  862. }
  863. }
  864. label.text = processed;
  865. #if !MOBILE
  866. if (selected)
  867. {
  868. int start = mSelectionStart - mDrawStart;
  869. int end = mSelectionEnd - mDrawStart;
  870. // Blank texture used by selection and caret
  871. if (mBlankTex == null)
  872. {
  873. mBlankTex = new Texture2D(2, 2, TextureFormat.ARGB32, false);
  874. for (int y = 0; y < 2; ++y)
  875. for (int x = 0; x < 2; ++x)
  876. mBlankTex.SetPixel(x, y, Color.white);
  877. mBlankTex.Apply();
  878. }
  879. // Create the selection highlight
  880. if (start != end)
  881. {
  882. if (mHighlight == null)
  883. {
  884. mHighlight = NGUITools.AddWidget<UITexture>(label.cachedGameObject);
  885. mHighlight.name = "Input Highlight";
  886. mHighlight.mainTexture = mBlankTex;
  887. mHighlight.fillGeometry = false;
  888. mHighlight.pivot = label.pivot;
  889. mHighlight.SetAnchor(label.cachedTransform);
  890. }
  891. else
  892. {
  893. mHighlight.pivot = label.pivot;
  894. mHighlight.MarkAsChanged();
  895. }
  896. }
  897. // Create the caret
  898. if (mCaret == null)
  899. {
  900. mCaret = NGUITools.AddWidget<UITexture>(label.cachedGameObject);
  901. mCaret.name = "Input Caret";
  902. mCaret.mainTexture = mBlankTex;
  903. mCaret.fillGeometry = false;
  904. mCaret.pivot = label.pivot;
  905. mCaret.SetAnchor(label.cachedTransform);
  906. }
  907. else
  908. {
  909. mCaret.pivot = label.pivot;
  910. mCaret.MarkAsChanged();
  911. mCaret.enabled = true;
  912. }
  913. // Fill the selection
  914. if (start != end)
  915. {
  916. label.PrintOverlay(start, end, mCaret.geometry, mHighlight.geometry, caretColor, selectionColor);
  917. mHighlight.enabled = mHighlight.geometry.hasVertices;
  918. }
  919. else
  920. {
  921. label.PrintOverlay(start, end, mCaret.geometry, null, caretColor, selectionColor);
  922. if (mHighlight != null) mHighlight.enabled = false;
  923. }
  924. // Reset the blinking time
  925. mNextBlink = RealTime.time + 0.5f;
  926. }
  927. else Cleanup();
  928. #endif
  929. }
  930. }
  931. /// <summary>
  932. /// Set the label's pivot to the left.
  933. /// </summary>
  934. protected void SetPivotToLeft ()
  935. {
  936. Vector2 po = NGUIMath.GetPivotOffset(mPivot);
  937. po.x = 0f;
  938. label.pivot = NGUIMath.GetPivot(po);
  939. }
  940. /// <summary>
  941. /// Set the label's pivot to the right.
  942. /// </summary>
  943. protected void SetPivotToRight ()
  944. {
  945. Vector2 po = NGUIMath.GetPivotOffset(mPivot);
  946. po.x = 1f;
  947. label.pivot = NGUIMath.GetPivot(po);
  948. }
  949. /// <summary>
  950. /// Restore the input label's pivot point.
  951. /// </summary>
  952. protected void RestoreLabelPivot ()
  953. {
  954. if (label != null && label.pivot != mPivot)
  955. label.pivot = mPivot;
  956. }
  957. /// <summary>
  958. /// Validate the specified input.
  959. /// </summary>
  960. protected char Validate (string text, int pos, char ch)
  961. {
  962. // Validation is disabled
  963. if (validation == Validation.None || !enabled) return ch;
  964. if (validation == Validation.Integer)
  965. {
  966. // Integer number validation
  967. if (ch >= '0' && ch <= '9') return ch;
  968. if (ch == '-' && pos == 0 && !text.Contains("-")) return ch;
  969. }
  970. else if (validation == Validation.Float)
  971. {
  972. // Floating-point number
  973. if (ch >= '0' && ch <= '9') return ch;
  974. if (ch == '-' && pos == 0 && !text.Contains("-")) return ch;
  975. if (ch == '.' && !text.Contains(".")) return ch;
  976. }
  977. else if (validation == Validation.Alphanumeric)
  978. {
  979. // All alphanumeric characters
  980. if (ch >= 'A' && ch <= 'Z') return ch;
  981. if (ch >= 'a' && ch <= 'z') return ch;
  982. if (ch >= '0' && ch <= '9') return ch;
  983. }
  984. else if (validation == Validation.Username)
  985. {
  986. // Lowercase and numbers
  987. if (ch >= 'A' && ch <= 'Z') return (char)(ch - 'A' + 'a');
  988. if (ch >= 'a' && ch <= 'z') return ch;
  989. if (ch >= '0' && ch <= '9') return ch;
  990. }
  991. else if (validation == Validation.Name)
  992. {
  993. char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
  994. char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
  995. if (ch >= 'a' && ch <= 'z')
  996. {
  997. // Space followed by a letter -- make sure it's capitalized
  998. if (lastChar == ' ') return (char)(ch - 'a' + 'A');
  999. return ch;
  1000. }
  1001. else if (ch >= 'A' && ch <= 'Z')
  1002. {
  1003. // Uppercase letters are only allowed after spaces (and apostrophes)
  1004. if (lastChar != ' ' && lastChar != '\'') return (char)(ch - 'A' + 'a');
  1005. return ch;
  1006. }
  1007. else if (ch == '\'')
  1008. {
  1009. // Don't allow more than one apostrophe
  1010. if (lastChar != ' ' && lastChar != '\'' && nextChar != '\'' && !text.Contains("'")) return ch;
  1011. }
  1012. else if (ch == ' ')
  1013. {
  1014. // Don't allow more than one space in a row
  1015. if (lastChar != ' ' && lastChar != '\'' && nextChar != ' ' && nextChar != '\'') return ch;
  1016. }
  1017. }
  1018. return (char)0;
  1019. }
  1020. /// <summary>
  1021. /// Execute the OnChange callback.
  1022. /// </summary>
  1023. protected void ExecuteOnChange ()
  1024. {
  1025. if (EventDelegate.IsValid(onChange))
  1026. {
  1027. current = this;
  1028. EventDelegate.Execute(onChange);
  1029. current = null;
  1030. }
  1031. }
  1032. }