InputTextField.cs 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Text.RegularExpressions;
  5. using UnityEngine;
  6. using FairyGUI.Utils;
  7. namespace FairyGUI
  8. {
  9. /// <summary>
  10. /// 接收用户输入的文本控件。因为支持直接输入表情,所以从RichTextField派生。
  11. /// </summary>
  12. public class InputTextField : RichTextField
  13. {
  14. /// <summary>
  15. ///
  16. /// </summary>
  17. public int maxLength { get; set; }
  18. /// <summary>
  19. /// 如果是true,则当文本获得焦点时,弹出键盘进行输入,如果是false则不会。
  20. /// 默认是使用Stage.keyboardInput的值。
  21. /// </summary>
  22. public bool keyboardInput { get; set; }
  23. /// <summary>
  24. ///
  25. /// </summary>
  26. public int keyboardType { get; set; }
  27. /// <summary>
  28. ///
  29. /// </summary>
  30. public bool hideInput { get; set; }
  31. /// <summary>
  32. ///
  33. /// </summary>
  34. public bool disableIME { get; set; }
  35. /// <summary>
  36. ///
  37. /// </summary>
  38. public bool mouseWheelEnabled { get; set; }
  39. /// <summary>
  40. ///
  41. /// </summary>
  42. public static Action<InputTextField, string> onCopy;
  43. /// <summary>
  44. ///
  45. /// </summary>
  46. public static Action<InputTextField> onPaste;
  47. /// <summary>
  48. ///
  49. /// </summary>
  50. public static PopupMenu contextMenu;
  51. string _text;
  52. string _restrict;
  53. Regex _restrictPattern;
  54. bool _displayAsPassword;
  55. string _promptText;
  56. string _decodedPromptText;
  57. int _border;
  58. int _corner;
  59. Color _borderColor;
  60. Color _backgroundColor;
  61. bool _editable;
  62. bool _editing;
  63. int _caretPosition;
  64. int _selectionStart;
  65. int _composing;
  66. char _highSurrogateChar;
  67. string _textBeforeEdit;
  68. EventListener _onChanged;
  69. EventListener _onSubmit;
  70. Shape _caret;
  71. SelectionShape _selectionShape;
  72. float _nextBlink;
  73. const int GUTTER_X = 2;
  74. const int GUTTER_Y = 2;
  75. public InputTextField()
  76. {
  77. gameObject.name = "InputTextField";
  78. _text = string.Empty;
  79. maxLength = 0;
  80. _editable = true;
  81. _composing = 0;
  82. keyboardInput = Stage.keyboardInput;
  83. _borderColor = Color.black;
  84. _backgroundColor = Color.clear;
  85. mouseWheelEnabled = true;
  86. this.tabStop = true;
  87. cursor = "text-ibeam";
  88. /* 因为InputTextField定义了ClipRect,而ClipRect是四周缩进了2个像素的(GUTTER),默认的点击测试
  89. * 是使用ClipRect的,那会造成无法点击四周的空白区域。所以这里自定义了一个HitArea
  90. */
  91. this.hitArea = new RectHitTest();
  92. this.touchChildren = false;
  93. onFocusIn.Add(__focusIn);
  94. onFocusOut.AddCapture(__focusOut);
  95. onKeyDown.Add(__keydown);
  96. onTouchBegin.AddCapture(__touchBegin);
  97. onTouchMove.AddCapture(__touchMove);
  98. onMouseWheel.Add(__mouseWheel);
  99. onClick.Add(__click);
  100. onRightClick.Add(__rightClick);
  101. }
  102. /// <summary>
  103. ///
  104. /// </summary>
  105. public EventListener onChanged
  106. {
  107. get { return _onChanged ?? (_onChanged = new EventListener(this, "onChanged")); }
  108. }
  109. /// <summary>
  110. ///
  111. /// </summary>
  112. public EventListener onSubmit
  113. {
  114. get { return _onSubmit ?? (_onSubmit = new EventListener(this, "onSubmit")); }
  115. }
  116. /// <summary>
  117. ///
  118. /// </summary>
  119. public override string text
  120. {
  121. get
  122. {
  123. return _text;
  124. }
  125. set
  126. {
  127. _text = value;
  128. ClearSelection();
  129. UpdateText();
  130. }
  131. }
  132. /// <summary>
  133. ///
  134. /// </summary>
  135. public override TextFormat textFormat
  136. {
  137. get
  138. {
  139. return base.textFormat;
  140. }
  141. set
  142. {
  143. base.textFormat = value;
  144. if (_editing)
  145. {
  146. _caret.height = textField.textFormat.size;
  147. _caret.DrawRect(0, Color.clear, textField.textFormat.color);
  148. }
  149. }
  150. }
  151. /// <summary>
  152. ///
  153. /// </summary>
  154. public string restrict
  155. {
  156. get { return _restrict; }
  157. set
  158. {
  159. _restrict = value;
  160. if (string.IsNullOrEmpty(_restrict))
  161. _restrictPattern = null;
  162. else
  163. _restrictPattern = new Regex(value);
  164. }
  165. }
  166. /// <summary>
  167. ///
  168. /// </summary>
  169. public int caretPosition
  170. {
  171. get
  172. {
  173. textField.Redraw();
  174. return _caretPosition;
  175. }
  176. set
  177. {
  178. SetSelection(value, 0);
  179. }
  180. }
  181. public int selectionBeginIndex
  182. {
  183. get { return _selectionStart < _caretPosition ? _selectionStart : _caretPosition; }
  184. }
  185. public int selectionEndIndex
  186. {
  187. get { return _selectionStart < _caretPosition ? _caretPosition : _selectionStart; }
  188. }
  189. /// <summary>
  190. ///
  191. /// </summary>
  192. public string promptText
  193. {
  194. get
  195. {
  196. return _promptText;
  197. }
  198. set
  199. {
  200. _promptText = value;
  201. if (!string.IsNullOrEmpty(_promptText))
  202. _decodedPromptText = UBBParser.inst.Parse(XMLUtils.EncodeString(_promptText));
  203. else
  204. _decodedPromptText = null;
  205. UpdateText();
  206. }
  207. }
  208. /// <summary>
  209. ///
  210. /// </summary>
  211. public bool displayAsPassword
  212. {
  213. get { return _displayAsPassword; }
  214. set
  215. {
  216. if (_displayAsPassword != value)
  217. {
  218. _displayAsPassword = value;
  219. UpdateText();
  220. }
  221. }
  222. }
  223. /// <summary>
  224. ///
  225. /// </summary>
  226. public bool editable
  227. {
  228. get { return _editable; }
  229. set
  230. {
  231. _editable = value;
  232. if (_caret != null)
  233. _caret.visible = _editable;
  234. }
  235. }
  236. /// <summary>
  237. ///
  238. /// </summary>
  239. public int border
  240. {
  241. get { return _border; }
  242. set
  243. {
  244. _border = value;
  245. UpdateShape();
  246. }
  247. }
  248. /// <summary>
  249. ///
  250. /// </summary>
  251. public int corner
  252. {
  253. get { return _corner; }
  254. set
  255. {
  256. _corner = value;
  257. UpdateShape();
  258. }
  259. }
  260. /// <summary>
  261. ///
  262. /// </summary>
  263. public Color borderColor
  264. {
  265. get { return _borderColor; }
  266. set
  267. {
  268. _borderColor = value;
  269. UpdateShape();
  270. }
  271. }
  272. /// <summary>
  273. ///
  274. /// </summary>
  275. public Color backgroundColor
  276. {
  277. get { return _backgroundColor; }
  278. set
  279. {
  280. _backgroundColor = value;
  281. UpdateShape();
  282. }
  283. }
  284. void UpdateShape()
  285. {
  286. if (_border > 0 || _backgroundColor.a > 0)
  287. {
  288. CreateGraphics();
  289. graphics.enabled = true;
  290. RoundedRectMesh mesh = graphics.GetMeshFactory<RoundedRectMesh>();
  291. mesh.lineWidth = _border;
  292. mesh.lineColor = _borderColor;
  293. mesh.fillColor = _backgroundColor;
  294. mesh.topLeftRadius = mesh.topRightRadius = mesh.bottomLeftRadius = mesh.bottomRightRadius = corner;
  295. graphics.SetMeshDirty();
  296. }
  297. else
  298. {
  299. if (graphics != null)
  300. graphics.enabled = false;
  301. }
  302. }
  303. /// <summary>
  304. ///
  305. /// </summary>
  306. /// <param name="start"></param>
  307. /// <param name="length">-1 means the rest count from start</param>
  308. public void SetSelection(int start, int length)
  309. {
  310. if (!_editing)
  311. Stage.inst.focus = this;
  312. _selectionStart = start;
  313. _caretPosition = length < 0 ? int.MaxValue : (start + length);
  314. if (!textField.Redraw())
  315. {
  316. int cnt = textField.charPositions.Count;
  317. if (_caretPosition >= cnt)
  318. _caretPosition = cnt - 1;
  319. if (_selectionStart >= cnt)
  320. _selectionStart = cnt - 1;
  321. UpdateCaret();
  322. }
  323. }
  324. /// <summary>
  325. ///
  326. /// </summary>
  327. /// <param name="value"></param>
  328. public void ReplaceSelection(string value)
  329. {
  330. if (keyboardInput && Stage.keyboardInput && !Stage.inst.keyboard.supportsCaret)
  331. {
  332. this.text = _text + value;
  333. OnChanged();
  334. return;
  335. }
  336. if (!_editing)
  337. Stage.inst.focus = this;
  338. textField.Redraw();
  339. int t0, t1;
  340. if (_selectionStart != _caretPosition)
  341. {
  342. if (_selectionStart < _caretPosition)
  343. {
  344. t0 = _selectionStart;
  345. t1 = _caretPosition;
  346. _caretPosition = _selectionStart;
  347. }
  348. else
  349. {
  350. t0 = _caretPosition;
  351. t1 = _selectionStart;
  352. _selectionStart = _caretPosition;
  353. }
  354. }
  355. else
  356. {
  357. if (string.IsNullOrEmpty(value))
  358. return;
  359. t0 = t1 = _caretPosition;
  360. }
  361. StringBuilder buffer = new StringBuilder();
  362. GetPartialText(0, t0, buffer);
  363. if (!string.IsNullOrEmpty(value))
  364. {
  365. value = ValidateInput(value);
  366. buffer.Append(value);
  367. _caretPosition += GetTextlength(value);
  368. }
  369. GetPartialText(t1 + _composing, -1, buffer);
  370. string newText = buffer.ToString();
  371. if (maxLength > 0)
  372. {
  373. string newText2 = TruncateText(newText, maxLength);
  374. if (newText2.Length != newText.Length)
  375. _caretPosition += (newText2.Length - newText.Length);
  376. newText = newText2;
  377. }
  378. this.text = newText;
  379. OnChanged();
  380. }
  381. /// <summary>
  382. ///
  383. /// </summary>
  384. /// <param name="value"></param>
  385. public void ReplaceText(string value)
  386. {
  387. if (value == _text)
  388. return;
  389. if (value == null)
  390. value = string.Empty;
  391. value = ValidateInput(value);
  392. if (maxLength > 0)
  393. value = TruncateText(value, maxLength);
  394. _caretPosition = value.Length;
  395. this.text = value;
  396. OnChanged();
  397. }
  398. void GetPartialText(int startIndex, int endIndex, StringBuilder buffer)
  399. {
  400. int elementCount = textField.htmlElements.Count;
  401. int lastIndex = startIndex;
  402. string tt;
  403. if (_displayAsPassword)
  404. tt = _text;
  405. else
  406. tt = textField.parsedText;
  407. if (endIndex < 0)
  408. endIndex = tt.Length;
  409. for (int i = 0; i < elementCount; i++)
  410. {
  411. HtmlElement element = textField.htmlElements[i];
  412. if (element.htmlObject != null && element.text != null)
  413. {
  414. if (element.charIndex >= startIndex && element.charIndex < endIndex)
  415. {
  416. buffer.Append(tt.Substring(lastIndex, element.charIndex - lastIndex));
  417. buffer.Append(element.text);
  418. lastIndex = element.charIndex + 1;
  419. }
  420. }
  421. }
  422. if (lastIndex < tt.Length)
  423. buffer.Append(tt.Substring(lastIndex, endIndex - lastIndex));
  424. }
  425. int GetTextlength(string value)
  426. {
  427. int textLen = value.Length;
  428. int ret = textLen;
  429. for (int i = 0; i < textLen; i++)
  430. {
  431. if (char.IsHighSurrogate(value[i]))
  432. ret--;
  433. }
  434. return ret;
  435. }
  436. string TruncateText(string value, int length)
  437. {
  438. int textLen = value.Length;
  439. int len = 0;
  440. int i = 0;
  441. while (i < textLen)
  442. {
  443. if (len == length)
  444. return value.Substring(0, i);
  445. if (char.IsHighSurrogate(value[i]))
  446. i++;
  447. i++;
  448. len++;
  449. }
  450. return value;
  451. }
  452. string ValidateInput(string source)
  453. {
  454. if (_restrict != null)
  455. {
  456. StringBuilder sb = new StringBuilder();
  457. Match mc = _restrictPattern.Match(source);
  458. int lastPos = 0;
  459. string s;
  460. while (mc != Match.Empty)
  461. {
  462. if (mc.Index != lastPos)
  463. {
  464. //保留tab和回车
  465. for (int i = lastPos; i < mc.Index; i++)
  466. {
  467. if (source[i] == '\n' || source[i] == '\t')
  468. sb.Append(source[i]);
  469. }
  470. }
  471. s = mc.ToString();
  472. lastPos = mc.Index + s.Length;
  473. sb.Append(s);
  474. mc = mc.NextMatch();
  475. }
  476. for (int i = lastPos; i < source.Length; i++)
  477. {
  478. if (source[i] == '\n' || source[i] == '\t')
  479. sb.Append(source[i]);
  480. }
  481. return sb.ToString();
  482. }
  483. else
  484. return source;
  485. }
  486. void UpdateText()
  487. {
  488. if (!_editing && _text.Length == 0 && !string.IsNullOrEmpty(_decodedPromptText))
  489. {
  490. textField.htmlText = _decodedPromptText;
  491. return;
  492. }
  493. if (_displayAsPassword)
  494. textField.text = EncodePasswordText(_text);
  495. else
  496. textField.text = _text;
  497. _composing = Input.compositionString.Length;
  498. if (_composing > 0)
  499. {
  500. StringBuilder buffer = new StringBuilder();
  501. GetPartialText(0, _caretPosition, buffer);
  502. buffer.Append(Input.compositionString);
  503. GetPartialText(_caretPosition, -1, buffer);
  504. textField.text = buffer.ToString();
  505. }
  506. }
  507. string EncodePasswordText(string value)
  508. {
  509. int textLen = value.Length;
  510. StringBuilder tmp = new StringBuilder(textLen);
  511. int i = 0;
  512. while (i < textLen)
  513. {
  514. char c = value[i];
  515. if (c == '\n')
  516. tmp.Append(c);
  517. else
  518. {
  519. if (char.IsHighSurrogate(c))
  520. i++;
  521. tmp.Append("*");
  522. }
  523. i++;
  524. }
  525. return tmp.ToString();
  526. }
  527. void ClearSelection()
  528. {
  529. if (_selectionStart != _caretPosition)
  530. {
  531. if (_selectionShape != null)
  532. _selectionShape.Clear();
  533. _selectionStart = _caretPosition;
  534. }
  535. }
  536. public string GetSelection()
  537. {
  538. if (_selectionStart == _caretPosition)
  539. return string.Empty;
  540. StringBuilder buffer = new StringBuilder();
  541. if (_selectionStart < _caretPosition)
  542. GetPartialText(_selectionStart, _caretPosition, buffer);
  543. else
  544. GetPartialText(_caretPosition, _selectionStart, buffer);
  545. return buffer.ToString();
  546. }
  547. void Scroll(int hScroll, int vScroll)
  548. {
  549. vScroll = Mathf.Clamp(vScroll, 0, textField.lines.Count - 1);
  550. TextField.LineInfo line = textField.lines[vScroll];
  551. hScroll = Mathf.Clamp(hScroll, 0, line.charCount - 1);
  552. TextField.CharPosition cp = GetCharPosition(line.charIndex + hScroll);
  553. Vector2 pt = GetCharLocation(cp);
  554. MoveContent(new Vector2(GUTTER_X - pt.x, GUTTER_Y - pt.y), false);
  555. }
  556. void AdjustCaret(TextField.CharPosition cp, bool moveSelectionHeader = false)
  557. {
  558. _caretPosition = cp.charIndex;
  559. if (moveSelectionHeader)
  560. _selectionStart = _caretPosition;
  561. UpdateCaret();
  562. }
  563. void UpdateCaret(bool forceUpdate = false)
  564. {
  565. TextField.CharPosition cp;
  566. if (_editing)
  567. cp = GetCharPosition(_caretPosition + Input.compositionString.Length);
  568. else
  569. cp = GetCharPosition(_caretPosition);
  570. Vector2 pos = GetCharLocation(cp);
  571. TextField.LineInfo line = textField.lines[cp.lineIndex];
  572. Vector2 offset = pos + textField.xy;
  573. if (offset.x < textField.textFormat.size)
  574. offset.x += Mathf.Min(50, _contentRect.width * 0.5f);
  575. else if (offset.x > _contentRect.width - GUTTER_X - textField.textFormat.size)
  576. offset.x -= Mathf.Min(50, _contentRect.width * 0.5f);
  577. if (offset.x < GUTTER_X)
  578. offset.x = GUTTER_X;
  579. else if (offset.x > _contentRect.width - GUTTER_X)
  580. offset.x = Mathf.Max(GUTTER_X, _contentRect.width - GUTTER_X);
  581. if (offset.y < GUTTER_Y)
  582. offset.y = GUTTER_Y;
  583. else if (offset.y + line.height >= _contentRect.height - GUTTER_Y)
  584. offset.y = Mathf.Max(GUTTER_Y, _contentRect.height - line.height - GUTTER_Y);
  585. MoveContent(offset - pos, forceUpdate);
  586. if (_editing)
  587. {
  588. _caret.position = textField.xy + pos;
  589. _caret.height = line.height > 0 ? line.height : textField.textFormat.size;
  590. if (_editable)
  591. {
  592. Vector2 cursorPos = _caret.LocalToWorld(new Vector2(0, _caret.height));
  593. cursorPos = StageCamera.main.WorldToScreenPoint(cursorPos);
  594. #if !UNITY_2019_OR_NEWER
  595. if (Stage.devicePixelRatio == 1)
  596. {
  597. #endif
  598. cursorPos.y = Screen.height - cursorPos.y;
  599. cursorPos = cursorPos / Stage.devicePixelRatio;
  600. Input.compositionCursorPos = cursorPos + new Vector2(0, 20);
  601. #if !UNITY_2019_OR_NEWER
  602. }
  603. else
  604. Input.compositionCursorPos = cursorPos - new Vector2(0, 20);
  605. #endif
  606. }
  607. _nextBlink = Time.time + 0.5f;
  608. _caret.graphics.enabled = true;
  609. UpdateSelection(cp);
  610. }
  611. }
  612. void MoveContent(Vector2 pos, bool forceUpdate)
  613. {
  614. float ox = textField.x;
  615. float oy = textField.y;
  616. float nx = pos.x;
  617. float ny = pos.y;
  618. float rectWidth = _contentRect.width - 1; //-1 to avoid cursor be clipped
  619. if (rectWidth - nx > textField.textWidth)
  620. nx = rectWidth - textField.textWidth;
  621. if (_contentRect.height - ny > textField.textHeight)
  622. ny = _contentRect.height - textField.textHeight;
  623. if (nx > 0)
  624. nx = 0;
  625. if (ny > 0)
  626. ny = 0;
  627. nx = (int)nx;
  628. ny = (int)ny;
  629. if (nx != ox || ny != oy || forceUpdate)
  630. {
  631. if (_caret != null)
  632. {
  633. _caret.SetXY(nx + _caret.x - ox, ny + _caret.y - oy);
  634. _selectionShape.SetXY(nx, ny);
  635. }
  636. textField.SetXY(nx, ny);
  637. List<HtmlElement> elements = textField.htmlElements;
  638. int count = elements.Count;
  639. for (int i = 0; i < count; i++)
  640. {
  641. HtmlElement element = elements[i];
  642. if (element.htmlObject != null)
  643. element.htmlObject.SetPosition(element.position.x + nx, element.position.y + ny);
  644. }
  645. }
  646. }
  647. void UpdateSelection(TextField.CharPosition cp)
  648. {
  649. if (_selectionStart == _caretPosition)
  650. {
  651. _selectionShape.Clear();
  652. return;
  653. }
  654. TextField.CharPosition start;
  655. if (_editing && Input.compositionString.Length > 0)
  656. {
  657. if (_selectionStart < _caretPosition)
  658. {
  659. cp = GetCharPosition(_caretPosition);
  660. start = GetCharPosition(_selectionStart);
  661. }
  662. else
  663. start = GetCharPosition(_selectionStart + Input.compositionString.Length);
  664. }
  665. else
  666. start = GetCharPosition(_selectionStart);
  667. if (start.charIndex > cp.charIndex)
  668. {
  669. TextField.CharPosition tmp = start;
  670. start = cp;
  671. cp = tmp;
  672. }
  673. Vector2 v1 = GetCharLocation(start);
  674. Vector2 v2 = GetCharLocation(cp);
  675. _selectionShape.rects.Clear();
  676. textField.GetLinesShape(start.lineIndex, v1.x, cp.lineIndex, v2.x, false, _selectionShape.rects);
  677. _selectionShape.Refresh();
  678. }
  679. TextField.CharPosition GetCharPosition(int caretIndex)
  680. {
  681. if (caretIndex < 0)
  682. caretIndex = 0;
  683. else if (caretIndex >= textField.charPositions.Count)
  684. caretIndex = textField.charPositions.Count - 1;
  685. return textField.charPositions[caretIndex];
  686. }
  687. /// <summary>
  688. /// 通过本地坐标获得字符索引位置
  689. /// </summary>
  690. /// <param name="location">本地坐标</param>
  691. /// <returns></returns>
  692. TextField.CharPosition GetCharPosition(Vector2 location)
  693. {
  694. if (textField.charPositions.Count <= 1)
  695. return textField.charPositions[0];
  696. location.x -= textField.x;
  697. location.y -= textField.y;
  698. List<TextField.LineInfo> lines = textField.lines;
  699. int len = lines.Count;
  700. TextField.LineInfo line;
  701. int i;
  702. for (i = 0; i < len; i++)
  703. {
  704. line = lines[i];
  705. if (line.y + line.height > location.y)
  706. break;
  707. }
  708. if (i == len)
  709. i = len - 1;
  710. int lineIndex = i;
  711. len = textField.charPositions.Count;
  712. TextField.CharPosition v;
  713. int firstInLine = -1;
  714. for (i = 0; i < len; i++)
  715. {
  716. v = textField.charPositions[i];
  717. if (v.lineIndex == lineIndex)
  718. {
  719. if (firstInLine == -1)
  720. firstInLine = i;
  721. if (v.offsetX + v.width * 0.5f > location.x)
  722. return v;
  723. }
  724. else if (firstInLine != -1)
  725. return v;
  726. }
  727. return textField.charPositions[i - 1];
  728. }
  729. /// <summary>
  730. /// 获得字符的坐标。
  731. /// </summary>
  732. /// <param name="cp"></param>
  733. /// <returns></returns>
  734. Vector2 GetCharLocation(TextField.CharPosition cp)
  735. {
  736. TextField.LineInfo line = textField.lines[cp.lineIndex];
  737. Vector2 pos;
  738. if (line.charCount == 0 || textField.charPositions.Count == 0)
  739. {
  740. if (textField.align == AlignType.Center)
  741. pos.x = (int)(_contentRect.width / 2);
  742. else
  743. pos.x = GUTTER_X;
  744. }
  745. else
  746. {
  747. TextField.CharPosition v = textField.charPositions[Math.Min(cp.charIndex, textField.charPositions.Count - 1)];
  748. pos.x = v.offsetX;
  749. }
  750. pos.y = line.y;
  751. return pos;
  752. }
  753. override internal void RefreshObjects()
  754. {
  755. base.RefreshObjects();
  756. if (_editing)
  757. {
  758. SetChildIndex(_selectionShape, 0);
  759. SetChildIndex(_caret, this.numChildren - 1);
  760. }
  761. int cnt = textField.charPositions.Count;
  762. if (_caretPosition >= cnt)
  763. _caretPosition = cnt - 1;
  764. if (_selectionStart >= cnt)
  765. _selectionStart = cnt - 1;
  766. UpdateCaret(true);
  767. }
  768. protected void OnChanged()
  769. {
  770. DispatchEvent("onChanged", null);
  771. TextInputHistory.inst.MarkChanged(this);
  772. }
  773. protected override void OnSizeChanged()
  774. {
  775. base.OnSizeChanged();
  776. Rect rect = _contentRect;
  777. rect.x += GUTTER_X;
  778. rect.y += GUTTER_Y;
  779. rect.width -= GUTTER_X * 2;
  780. rect.height -= GUTTER_Y * 2;
  781. this.clipRect = rect;
  782. ((RectHitTest)this.hitArea).rect = _contentRect;
  783. }
  784. public override void Update(UpdateContext context)
  785. {
  786. base.Update(context);
  787. if (_editing)
  788. {
  789. if (_nextBlink < Time.time)
  790. {
  791. _nextBlink = Time.time + 0.5f;
  792. _caret.graphics.enabled = !_caret.graphics.enabled;
  793. }
  794. }
  795. }
  796. public override void Dispose()
  797. {
  798. if ((_flags & Flags.Disposed) != 0)
  799. return;
  800. _editing = false;
  801. if (_caret != null)
  802. {
  803. _caret.Dispose();
  804. _selectionShape.Dispose();
  805. }
  806. base.Dispose();
  807. }
  808. void DoCopy(string value)
  809. {
  810. if (onCopy != null)
  811. {
  812. onCopy(this, value);
  813. return;
  814. }
  815. #if UNITY_WEBPLAYER || UNITY_WEBGL || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_EDITOR
  816. TextEditor textEditor = new TextEditor();
  817. #if UNITY_5_3_OR_NEWER
  818. textEditor.text = value;
  819. #else
  820. textEditor.content = new GUIContent(value);
  821. #endif
  822. textEditor.OnFocus();
  823. textEditor.Copy();
  824. #endif
  825. }
  826. void DoPaste()
  827. {
  828. if (onPaste != null)
  829. {
  830. onPaste(this);
  831. return;
  832. }
  833. #if UNITY_WEBPLAYER || UNITY_WEBGL || UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX || UNITY_EDITOR
  834. TextEditor textEditor = new TextEditor();
  835. #if UNITY_5_3_OR_NEWER
  836. textEditor.text = string.Empty;
  837. #else
  838. textEditor.content = new GUIContent(string.Empty);
  839. #endif
  840. textEditor.multiline = !textField.singleLine;
  841. textEditor.Paste();
  842. #if UNITY_5_3_OR_NEWER
  843. string value = textEditor.text;
  844. #else
  845. string value = textEditor.content.text;
  846. #endif
  847. if (!string.IsNullOrEmpty(value))
  848. ReplaceSelection(value);
  849. #endif
  850. }
  851. void CreateCaret()
  852. {
  853. _caret = new Shape();
  854. _caret.gameObject.name = "Caret";
  855. _caret.touchable = false;
  856. _caret._flags |= Flags.SkipBatching;
  857. _caret.xy = textField.xy;
  858. _selectionShape = new SelectionShape();
  859. _selectionShape.gameObject.name = "Selection";
  860. _selectionShape.color = UIConfig.inputHighlightColor;
  861. _selectionShape._flags |= Flags.SkipBatching;
  862. _selectionShape.touchable = false;
  863. _selectionShape.xy = textField.xy;
  864. }
  865. void __touchBegin(EventContext context)
  866. {
  867. if (!_editing || textField.charPositions.Count <= 1
  868. || keyboardInput && Stage.keyboardInput && !Stage.inst.keyboard.supportsCaret
  869. || context.inputEvent.button != 0)
  870. return;
  871. ClearSelection();
  872. Vector3 v = Stage.inst.touchPosition;
  873. v = this.GlobalToLocal(v);
  874. TextField.CharPosition cp = GetCharPosition(v);
  875. AdjustCaret(cp, true);
  876. context.CaptureTouch();
  877. }
  878. void __touchMove(EventContext context)
  879. {
  880. if (!_editing)
  881. return;
  882. Vector3 v = Stage.inst.touchPosition;
  883. v = this.GlobalToLocal(v);
  884. if (float.IsNaN(v.x))
  885. return;
  886. TextField.CharPosition cp = GetCharPosition(v);
  887. if (cp.charIndex != _caretPosition)
  888. AdjustCaret(cp);
  889. }
  890. void __mouseWheel(EventContext context)
  891. {
  892. if (_editing && mouseWheelEnabled)
  893. {
  894. context.StopPropagation();
  895. TextField.CharPosition cp = GetCharPosition(new Vector2(GUTTER_X, GUTTER_Y));
  896. int vScroll = cp.lineIndex;
  897. int hScroll = cp.charIndex - textField.lines[cp.lineIndex].charIndex;
  898. if (context.inputEvent.mouseWheelDelta < 0)
  899. vScroll--;
  900. else
  901. vScroll++;
  902. Scroll(hScroll, vScroll);
  903. }
  904. }
  905. void __focusIn(EventContext context)
  906. {
  907. if (!Application.isPlaying)
  908. return;
  909. _editing = true;
  910. _textBeforeEdit = _text;
  911. if (_caret == null)
  912. CreateCaret();
  913. if (!string.IsNullOrEmpty(_promptText))
  914. UpdateText();
  915. float caretSize;
  916. //如果界面缩小过,光标很容易看不见,这里放大一下
  917. if (UIConfig.inputCaretSize == 1 && UIContentScaler.scaleFactor < 1)
  918. caretSize = UIConfig.inputCaretSize / UIContentScaler.scaleFactor;
  919. else
  920. caretSize = UIConfig.inputCaretSize;
  921. _caret.SetSize(caretSize, textField.textFormat.size);
  922. _caret.DrawRect(0, Color.clear, textField.textFormat.color);
  923. _caret.visible = _editable;
  924. AddChild(_caret);
  925. _selectionShape.Clear();
  926. AddChildAt(_selectionShape, 0);
  927. if (!textField.Redraw())
  928. {
  929. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  930. AdjustCaret(cp);
  931. }
  932. if (Stage.keyboardInput)
  933. {
  934. if (keyboardInput)
  935. {
  936. if (_editable)
  937. Stage.inst.OpenKeyboard(_text, false, _displayAsPassword ? false : !textField.singleLine,
  938. _displayAsPassword, false, null, keyboardType, hideInput);
  939. SetSelection(0, -1);
  940. }
  941. }
  942. else
  943. {
  944. if (!disableIME && !_displayAsPassword)
  945. Input.imeCompositionMode = IMECompositionMode.On;
  946. else
  947. Input.imeCompositionMode = IMECompositionMode.Off;
  948. _composing = 0;
  949. if ((string)context.data == "key") //select all if got focus by tab key
  950. SetSelection(0, -1);
  951. TextInputHistory.inst.StartRecord(this);
  952. }
  953. }
  954. void __focusOut(EventContext contxt)
  955. {
  956. if (!_editing)
  957. return;
  958. _editing = false;
  959. if (Stage.keyboardInput)
  960. {
  961. if (keyboardInput)
  962. Stage.inst.CloseKeyboard();
  963. }
  964. else
  965. {
  966. Input.imeCompositionMode = IMECompositionMode.Auto;
  967. TextInputHistory.inst.StopRecord(this);
  968. }
  969. if (!string.IsNullOrEmpty(_promptText))
  970. UpdateText();
  971. _caret.RemoveFromParent();
  972. _selectionShape.RemoveFromParent();
  973. if (contextMenu != null && contextMenu.contentPane.onStage)
  974. contextMenu.Hide();
  975. }
  976. void __keydown(EventContext context)
  977. {
  978. if (!_editing)
  979. return;
  980. if (HandleKey(context.inputEvent))
  981. context.StopPropagation();
  982. }
  983. bool HandleKey(InputEvent evt)
  984. {
  985. bool keyCodeHandled = true;
  986. switch (evt.keyCode)
  987. {
  988. case KeyCode.Backspace:
  989. {
  990. if (evt.command)
  991. {
  992. //for mac:CMD+Backspace=Delete
  993. if (_selectionStart == _caretPosition && _caretPosition < textField.charPositions.Count - 1)
  994. _selectionStart = _caretPosition + 1;
  995. }
  996. else
  997. {
  998. if (_selectionStart == _caretPosition && _caretPosition > 0)
  999. _selectionStart = _caretPosition - 1;
  1000. }
  1001. if (_editable)
  1002. ReplaceSelection(null);
  1003. break;
  1004. }
  1005. case KeyCode.Delete:
  1006. {
  1007. if (_selectionStart == _caretPosition && _caretPosition < textField.charPositions.Count - 1)
  1008. _selectionStart = _caretPosition + 1;
  1009. if (_editable)
  1010. ReplaceSelection(null);
  1011. break;
  1012. }
  1013. case KeyCode.LeftArrow:
  1014. {
  1015. if (!evt.shift)
  1016. ClearSelection();
  1017. if (_caretPosition > 0)
  1018. {
  1019. if (evt.command) //mac keyboard
  1020. {
  1021. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  1022. TextField.LineInfo line = textField.lines[cp.lineIndex];
  1023. cp = GetCharPosition(new Vector2(int.MinValue, line.y + textField.y));
  1024. AdjustCaret(cp, !evt.shift);
  1025. }
  1026. else
  1027. {
  1028. TextField.CharPosition cp = GetCharPosition(_caretPosition - 1);
  1029. AdjustCaret(cp, !evt.shift);
  1030. }
  1031. }
  1032. break;
  1033. }
  1034. case KeyCode.RightArrow:
  1035. {
  1036. if (!evt.shift)
  1037. ClearSelection();
  1038. if (_caretPosition < textField.charPositions.Count - 1)
  1039. {
  1040. if (evt.command)
  1041. {
  1042. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  1043. TextField.LineInfo line = textField.lines[cp.lineIndex];
  1044. cp = GetCharPosition(new Vector2(int.MaxValue, line.y + textField.y));
  1045. AdjustCaret(cp, !evt.shift);
  1046. }
  1047. else
  1048. {
  1049. TextField.CharPosition cp = GetCharPosition(_caretPosition + 1);
  1050. AdjustCaret(cp, !evt.shift);
  1051. }
  1052. }
  1053. break;
  1054. }
  1055. case KeyCode.UpArrow:
  1056. {
  1057. if (!evt.shift)
  1058. ClearSelection();
  1059. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  1060. if (cp.lineIndex > 0)
  1061. {
  1062. TextField.LineInfo line = textField.lines[cp.lineIndex - 1];
  1063. cp = GetCharPosition(new Vector2(_caret.x, line.y + textField.y));
  1064. AdjustCaret(cp, !evt.shift);
  1065. }
  1066. break;
  1067. }
  1068. case KeyCode.DownArrow:
  1069. {
  1070. if (!evt.shift)
  1071. ClearSelection();
  1072. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  1073. if (cp.lineIndex == textField.lines.Count - 1)
  1074. cp.charIndex = textField.charPositions.Count - 1;
  1075. else
  1076. {
  1077. TextField.LineInfo line = textField.lines[cp.lineIndex + 1];
  1078. cp = GetCharPosition(new Vector2(_caret.x, line.y + textField.y));
  1079. }
  1080. AdjustCaret(cp, !evt.shift);
  1081. break;
  1082. }
  1083. case KeyCode.PageUp:
  1084. {
  1085. ClearSelection();
  1086. break;
  1087. }
  1088. case KeyCode.PageDown:
  1089. {
  1090. ClearSelection();
  1091. break;
  1092. }
  1093. case KeyCode.Home:
  1094. {
  1095. if (!evt.shift)
  1096. ClearSelection();
  1097. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  1098. TextField.LineInfo line = textField.lines[cp.lineIndex];
  1099. cp = GetCharPosition(new Vector2(int.MinValue, line.y + textField.y));
  1100. AdjustCaret(cp, !evt.shift);
  1101. break;
  1102. }
  1103. case KeyCode.End:
  1104. {
  1105. if (!evt.shift)
  1106. ClearSelection();
  1107. TextField.CharPosition cp = GetCharPosition(_caretPosition);
  1108. TextField.LineInfo line = textField.lines[cp.lineIndex];
  1109. cp = GetCharPosition(new Vector2(int.MaxValue, line.y + textField.y));
  1110. AdjustCaret(cp, !evt.shift);
  1111. break;
  1112. }
  1113. //Select All
  1114. case KeyCode.A:
  1115. {
  1116. if (evt.ctrlOrCmd)
  1117. {
  1118. _selectionStart = 0;
  1119. AdjustCaret(GetCharPosition(int.MaxValue));
  1120. }
  1121. break;
  1122. }
  1123. //Copy
  1124. case KeyCode.C:
  1125. {
  1126. if (evt.ctrlOrCmd && !_displayAsPassword)
  1127. {
  1128. string s = GetSelection();
  1129. if (!string.IsNullOrEmpty(s))
  1130. DoCopy(s);
  1131. }
  1132. break;
  1133. }
  1134. //Paste
  1135. case KeyCode.V:
  1136. {
  1137. if (evt.ctrlOrCmd && _editable)
  1138. DoPaste();
  1139. break;
  1140. }
  1141. //Cut
  1142. case KeyCode.X:
  1143. {
  1144. if (evt.ctrlOrCmd && !_displayAsPassword)
  1145. {
  1146. string s = GetSelection();
  1147. if (!string.IsNullOrEmpty(s))
  1148. {
  1149. DoCopy(s);
  1150. if (_editable)
  1151. ReplaceSelection(null);
  1152. }
  1153. }
  1154. break;
  1155. }
  1156. case KeyCode.Z:
  1157. {
  1158. if (evt.ctrlOrCmd && _editable)
  1159. {
  1160. if (evt.shift)
  1161. TextInputHistory.inst.Redo(this);
  1162. else
  1163. TextInputHistory.inst.Undo(this);
  1164. }
  1165. break;
  1166. }
  1167. case KeyCode.Y:
  1168. {
  1169. if (evt.ctrlOrCmd && _editable)
  1170. TextInputHistory.inst.Redo(this);
  1171. break;
  1172. }
  1173. case KeyCode.Return:
  1174. case KeyCode.KeypadEnter:
  1175. {
  1176. if (textField.singleLine)
  1177. {
  1178. Stage.inst.focus = parent;
  1179. DispatchEvent("onSubmit", null);
  1180. DispatchEvent("onKeyDown", null); //for backward compatibility
  1181. }
  1182. break;
  1183. }
  1184. case KeyCode.Tab:
  1185. {
  1186. if (textField.singleLine)
  1187. {
  1188. Stage.inst.DoKeyNavigate(evt.shift);
  1189. keyCodeHandled = false;
  1190. }
  1191. break;
  1192. }
  1193. case KeyCode.Escape:
  1194. {
  1195. this.text = _textBeforeEdit;
  1196. Stage.inst.focus = parent;
  1197. break;
  1198. }
  1199. default:
  1200. keyCodeHandled = (int)evt.keyCode <= 272 && !evt.ctrlOrCmd;
  1201. break;
  1202. }
  1203. char c = evt.character;
  1204. if (c != 0)
  1205. {
  1206. if (evt.ctrlOrCmd)
  1207. return true;
  1208. if (c == '\r' || c == 3)
  1209. c = '\n';
  1210. if (c == 25)/*shift+tab*/
  1211. c = '\t';
  1212. if (c == 27/*escape*/ || textField.singleLine && (c == '\n' || c == '\t'))
  1213. return true;
  1214. if (char.IsHighSurrogate(c))
  1215. {
  1216. _highSurrogateChar = c;
  1217. return true;
  1218. }
  1219. if (_editable)
  1220. {
  1221. if (char.IsLowSurrogate(c))
  1222. ReplaceSelection(char.ConvertFromUtf32(((int)c & 0x03FF) + ((((int)_highSurrogateChar & 0x03FF) + 0x40) << 10)));
  1223. else
  1224. ReplaceSelection(c.ToString());
  1225. }
  1226. return true;
  1227. }
  1228. else
  1229. {
  1230. if (Input.compositionString.Length > 0 && _editable)
  1231. {
  1232. int composing = _composing;
  1233. _composing = Input.compositionString.Length;
  1234. StringBuilder buffer = new StringBuilder();
  1235. GetPartialText(0, _caretPosition, buffer);
  1236. buffer.Append(Input.compositionString);
  1237. GetPartialText(_caretPosition + composing, -1, buffer);
  1238. textField.text = buffer.ToString();
  1239. }
  1240. return keyCodeHandled;
  1241. }
  1242. }
  1243. internal void CheckComposition()
  1244. {
  1245. if (_composing != 0 && Input.compositionString.Length == 0)
  1246. UpdateText();
  1247. }
  1248. void __click(EventContext context)
  1249. {
  1250. if (_editing && context.inputEvent.isDoubleClick)
  1251. {
  1252. context.StopPropagation();
  1253. _selectionStart = 0;
  1254. AdjustCaret(GetCharPosition(int.MaxValue));
  1255. }
  1256. }
  1257. void __rightClick(EventContext context)
  1258. {
  1259. if (contextMenu != null)
  1260. {
  1261. context.StopPropagation();
  1262. contextMenu.Show();
  1263. }
  1264. }
  1265. }
  1266. class TextInputHistory
  1267. {
  1268. static TextInputHistory _inst;
  1269. public static TextInputHistory inst
  1270. {
  1271. get
  1272. {
  1273. if (_inst == null)
  1274. _inst = new TextInputHistory();
  1275. return _inst;
  1276. }
  1277. }
  1278. List<string> _undoBuffer;
  1279. List<string> _redoBuffer;
  1280. string _currentText;
  1281. InputTextField _textField;
  1282. bool _lock;
  1283. int _changedFrame;
  1284. public const int maxHistoryLength = 5;
  1285. public TextInputHistory()
  1286. {
  1287. _undoBuffer = new List<string>();
  1288. _redoBuffer = new List<string>();
  1289. }
  1290. public void StartRecord(InputTextField textField)
  1291. {
  1292. _undoBuffer.Clear();
  1293. _redoBuffer.Clear();
  1294. _textField = textField;
  1295. _lock = false;
  1296. _currentText = textField.text;
  1297. _changedFrame = 0;
  1298. }
  1299. public void MarkChanged(InputTextField textField)
  1300. {
  1301. if (_textField != textField)
  1302. return;
  1303. if (_lock)
  1304. return;
  1305. string newText = _textField.text;
  1306. if (_currentText == newText)
  1307. return;
  1308. if (_changedFrame != Time.frameCount)
  1309. {
  1310. _changedFrame = Time.frameCount;
  1311. _undoBuffer.Add(_currentText);
  1312. if (_undoBuffer.Count > maxHistoryLength)
  1313. _undoBuffer.RemoveAt(0);
  1314. }
  1315. else
  1316. {
  1317. int cnt = _undoBuffer.Count;
  1318. if (cnt > 0 && newText == _undoBuffer[cnt - 1])
  1319. _undoBuffer.RemoveAt(cnt - 1);
  1320. }
  1321. _currentText = newText;
  1322. }
  1323. public void StopRecord(InputTextField textField)
  1324. {
  1325. if (_textField != textField)
  1326. return;
  1327. _undoBuffer.Clear();
  1328. _redoBuffer.Clear();
  1329. _textField = null;
  1330. _currentText = null;
  1331. }
  1332. public void Undo(InputTextField textField)
  1333. {
  1334. if (_textField != textField)
  1335. return;
  1336. if (_undoBuffer.Count == 0)
  1337. return;
  1338. string text = _undoBuffer[_undoBuffer.Count - 1];
  1339. _undoBuffer.RemoveAt(_undoBuffer.Count - 1);
  1340. _redoBuffer.Add(_currentText);
  1341. _lock = true;
  1342. int caretPos = _textField.caretPosition;
  1343. _textField.text = text;
  1344. int dlen = text.Length - _currentText.Length;
  1345. if (dlen < 0)
  1346. _textField.caretPosition = caretPos + dlen;
  1347. _currentText = text;
  1348. _lock = false;
  1349. }
  1350. public void Redo(InputTextField textField)
  1351. {
  1352. if (_textField != textField)
  1353. return;
  1354. if (_redoBuffer.Count == 0)
  1355. return;
  1356. string text = _redoBuffer[_redoBuffer.Count - 1];
  1357. _redoBuffer.RemoveAt(_redoBuffer.Count - 1);
  1358. _undoBuffer.Add(_currentText);
  1359. _lock = true;
  1360. int caretPos = _textField.caretPosition;
  1361. _textField.text = text;
  1362. int dlen = text.Length - _currentText.Length;
  1363. if (dlen > 0)
  1364. _textField.caretPosition = caretPos + dlen;
  1365. _currentText = text;
  1366. _lock = false;
  1367. }
  1368. }
  1369. }