PageRenderTime 51ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

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

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