TextBox.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using Avalonia.Input.Platform;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Reactive.Linq;
  8. using Avalonia.Controls.Presenters;
  9. using Avalonia.Controls.Primitives;
  10. using Avalonia.Controls.Utils;
  11. using Avalonia.Input;
  12. using Avalonia.Interactivity;
  13. using Avalonia.Media;
  14. using Avalonia.Metadata;
  15. using Avalonia.Data;
  16. namespace Avalonia.Controls
  17. {
  18. public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
  19. {
  20. public static readonly StyledProperty<bool> AcceptsReturnProperty =
  21. AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsReturn));
  22. public static readonly StyledProperty<bool> AcceptsTabProperty =
  23. AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsTab));
  24. public static readonly DirectProperty<TextBox, int> CaretIndexProperty =
  25. AvaloniaProperty.RegisterDirect<TextBox, int>(
  26. nameof(CaretIndex),
  27. o => o.CaretIndex,
  28. (o, v) => o.CaretIndex = v);
  29. public static readonly StyledProperty<bool> IsReadOnlyProperty =
  30. AvaloniaProperty.Register<TextBox, bool>(nameof(IsReadOnly));
  31. public static readonly DirectProperty<TextBox, int> SelectionStartProperty =
  32. AvaloniaProperty.RegisterDirect<TextBox, int>(
  33. nameof(SelectionStart),
  34. o => o.SelectionStart,
  35. (o, v) => o.SelectionStart = v);
  36. public static readonly DirectProperty<TextBox, int> SelectionEndProperty =
  37. AvaloniaProperty.RegisterDirect<TextBox, int>(
  38. nameof(SelectionEnd),
  39. o => o.SelectionEnd,
  40. (o, v) => o.SelectionEnd = v);
  41. public static readonly DirectProperty<TextBox, string> TextProperty =
  42. TextBlock.TextProperty.AddOwner<TextBox>(
  43. o => o.Text,
  44. (o, v) => o.Text = v,
  45. defaultBindingMode: BindingMode.TwoWay,
  46. enableDataValidation: true);
  47. public static readonly StyledProperty<TextAlignment> TextAlignmentProperty =
  48. TextBlock.TextAlignmentProperty.AddOwner<TextBox>();
  49. public static readonly StyledProperty<TextWrapping> TextWrappingProperty =
  50. TextBlock.TextWrappingProperty.AddOwner<TextBox>();
  51. public static readonly StyledProperty<string> WatermarkProperty =
  52. AvaloniaProperty.Register<TextBox, string>(nameof(Watermark));
  53. public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
  54. AvaloniaProperty.Register<TextBox, bool>(nameof(UseFloatingWatermark));
  55. struct UndoRedoState : IEquatable<UndoRedoState>
  56. {
  57. public string Text { get; }
  58. public int CaretPosition { get; }
  59. public UndoRedoState(string text, int caretPosition)
  60. {
  61. Text = text;
  62. CaretPosition = caretPosition;
  63. }
  64. public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text);
  65. }
  66. private string _text;
  67. private int _caretIndex;
  68. private int _selectionStart;
  69. private int _selectionEnd;
  70. private TextPresenter _presenter;
  71. private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
  72. private bool _isUndoingRedoing;
  73. private bool _ignoreTextChanges;
  74. private static readonly string[] invalidCharacters = new String[1]{"\u007f"};
  75. static TextBox()
  76. {
  77. FocusableProperty.OverrideDefaultValue(typeof(TextBox), true);
  78. }
  79. public TextBox()
  80. {
  81. var horizontalScrollBarVisibility = Observable.CombineLatest(
  82. this.GetObservable(AcceptsReturnProperty),
  83. this.GetObservable(TextWrappingProperty),
  84. (acceptsReturn, wrapping) =>
  85. {
  86. if (acceptsReturn)
  87. {
  88. return wrapping == TextWrapping.NoWrap ?
  89. ScrollBarVisibility.Auto :
  90. ScrollBarVisibility.Disabled;
  91. }
  92. else
  93. {
  94. return ScrollBarVisibility.Hidden;
  95. }
  96. });
  97. Bind(
  98. ScrollViewer.HorizontalScrollBarVisibilityProperty,
  99. horizontalScrollBarVisibility,
  100. BindingPriority.Style);
  101. _undoRedoHelper = new UndoRedoHelper<UndoRedoState>(this);
  102. }
  103. public bool AcceptsReturn
  104. {
  105. get { return GetValue(AcceptsReturnProperty); }
  106. set { SetValue(AcceptsReturnProperty, value); }
  107. }
  108. public bool AcceptsTab
  109. {
  110. get { return GetValue(AcceptsTabProperty); }
  111. set { SetValue(AcceptsTabProperty, value); }
  112. }
  113. public int CaretIndex
  114. {
  115. get
  116. {
  117. return _caretIndex;
  118. }
  119. set
  120. {
  121. value = CoerceCaretIndex(value);
  122. SetAndRaise(CaretIndexProperty, ref _caretIndex, value);
  123. UndoRedoState state;
  124. if (_undoRedoHelper.TryGetLastState(out state) && state.Text == Text)
  125. _undoRedoHelper.UpdateLastState();
  126. }
  127. }
  128. public bool IsReadOnly
  129. {
  130. get { return GetValue(IsReadOnlyProperty); }
  131. set { SetValue(IsReadOnlyProperty, value); }
  132. }
  133. public int SelectionStart
  134. {
  135. get
  136. {
  137. return _selectionStart;
  138. }
  139. set
  140. {
  141. value = CoerceCaretIndex(value);
  142. SetAndRaise(SelectionStartProperty, ref _selectionStart, value);
  143. if (SelectionStart == SelectionEnd)
  144. {
  145. CaretIndex = SelectionStart;
  146. }
  147. }
  148. }
  149. public int SelectionEnd
  150. {
  151. get
  152. {
  153. return _selectionEnd;
  154. }
  155. set
  156. {
  157. value = CoerceCaretIndex(value);
  158. SetAndRaise(SelectionEndProperty, ref _selectionEnd, value);
  159. if (SelectionStart == SelectionEnd)
  160. {
  161. CaretIndex = SelectionEnd;
  162. }
  163. }
  164. }
  165. [Content]
  166. public string Text
  167. {
  168. get { return _text; }
  169. set
  170. {
  171. if (!_ignoreTextChanges)
  172. {
  173. CaretIndex = CoerceCaretIndex(CaretIndex, value?.Length ?? 0);
  174. if (SetAndRaise(TextProperty, ref _text, value) && !_isUndoingRedoing)
  175. {
  176. _undoRedoHelper.Clear();
  177. }
  178. }
  179. }
  180. }
  181. public TextAlignment TextAlignment
  182. {
  183. get { return GetValue(TextAlignmentProperty); }
  184. set { SetValue(TextAlignmentProperty, value); }
  185. }
  186. public string Watermark
  187. {
  188. get { return GetValue(WatermarkProperty); }
  189. set { SetValue(WatermarkProperty, value); }
  190. }
  191. public bool UseFloatingWatermark
  192. {
  193. get { return GetValue(UseFloatingWatermarkProperty); }
  194. set { SetValue(UseFloatingWatermarkProperty, value); }
  195. }
  196. public TextWrapping TextWrapping
  197. {
  198. get { return GetValue(TextWrappingProperty); }
  199. set { SetValue(TextWrappingProperty, value); }
  200. }
  201. protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
  202. {
  203. _presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter");
  204. _presenter.Cursor = new Cursor(StandardCursorType.Ibeam);
  205. if(IsFocused)
  206. {
  207. _presenter.ShowCaret();
  208. }
  209. }
  210. protected override void OnGotFocus(GotFocusEventArgs e)
  211. {
  212. base.OnGotFocus(e);
  213. // when navigating to a textbox via the tab key, select all text if
  214. // 1) this textbox is *not* a multiline textbox
  215. // 2) this textbox has any text to select
  216. if (e.NavigationMethod == NavigationMethod.Tab &&
  217. !AcceptsReturn &&
  218. Text?.Length > 0)
  219. {
  220. SelectionStart = 0;
  221. SelectionEnd = Text.Length;
  222. }
  223. else
  224. {
  225. _presenter?.ShowCaret();
  226. }
  227. e.Handled = true;
  228. }
  229. protected override void OnLostFocus(RoutedEventArgs e)
  230. {
  231. base.OnLostFocus(e);
  232. SelectionStart = 0;
  233. SelectionEnd = 0;
  234. _presenter?.HideCaret();
  235. }
  236. protected override void OnTextInput(TextInputEventArgs e)
  237. {
  238. if (!e.Handled)
  239. {
  240. HandleTextInput(e.Text);
  241. e.Handled = true;
  242. }
  243. }
  244. private void HandleTextInput(string input)
  245. {
  246. if (!IsReadOnly)
  247. {
  248. input = RemoveInvalidCharacters(input);
  249. string text = Text ?? string.Empty;
  250. int caretIndex = CaretIndex;
  251. if (!string.IsNullOrEmpty(input))
  252. {
  253. DeleteSelection();
  254. caretIndex = CaretIndex;
  255. text = Text ?? string.Empty;
  256. SetTextInternal(text.Substring(0, caretIndex) + input + text.Substring(caretIndex));
  257. CaretIndex += input.Length;
  258. SelectionStart = SelectionEnd = CaretIndex;
  259. _undoRedoHelper.DiscardRedo();
  260. }
  261. }
  262. }
  263. public string RemoveInvalidCharacters(string text)
  264. {
  265. for (var i = 0; i < invalidCharacters.Length; i++)
  266. {
  267. text = text.Replace(invalidCharacters[i], string.Empty);
  268. }
  269. return text;
  270. }
  271. private async void Copy()
  272. {
  273. await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)))
  274. .SetTextAsync(GetSelection());
  275. }
  276. private async void Paste()
  277. {
  278. var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync();
  279. if (text == null)
  280. {
  281. return;
  282. }
  283. _undoRedoHelper.Snapshot();
  284. HandleTextInput(text);
  285. }
  286. protected override void OnKeyDown(KeyEventArgs e)
  287. {
  288. string text = Text ?? string.Empty;
  289. int caretIndex = CaretIndex;
  290. bool movement = false;
  291. bool handled = false;
  292. var modifiers = e.Modifiers;
  293. switch (e.Key)
  294. {
  295. case Key.A:
  296. if (modifiers == InputModifiers.Control)
  297. {
  298. SelectAll();
  299. handled = true;
  300. }
  301. break;
  302. case Key.C:
  303. if (modifiers == InputModifiers.Control)
  304. {
  305. Copy();
  306. handled = true;
  307. }
  308. break;
  309. case Key.X:
  310. if (modifiers == InputModifiers.Control)
  311. {
  312. Copy();
  313. DeleteSelection();
  314. handled = true;
  315. }
  316. break;
  317. case Key.V:
  318. if (modifiers == InputModifiers.Control)
  319. {
  320. Paste();
  321. handled = true;
  322. }
  323. break;
  324. case Key.Z:
  325. if (modifiers == InputModifiers.Control)
  326. {
  327. try
  328. {
  329. _isUndoingRedoing = true;
  330. _undoRedoHelper.Undo();
  331. }
  332. finally
  333. {
  334. _isUndoingRedoing = false;
  335. }
  336. handled = true;
  337. }
  338. break;
  339. case Key.Y:
  340. if (modifiers == InputModifiers.Control)
  341. {
  342. try
  343. {
  344. _isUndoingRedoing = true;
  345. _undoRedoHelper.Redo();
  346. }
  347. finally
  348. {
  349. _isUndoingRedoing = false;
  350. }
  351. handled = true;
  352. }
  353. break;
  354. case Key.Left:
  355. MoveHorizontal(-1, modifiers);
  356. movement = true;
  357. break;
  358. case Key.Right:
  359. MoveHorizontal(1, modifiers);
  360. movement = true;
  361. break;
  362. case Key.Up:
  363. movement = MoveVertical(-1, modifiers);
  364. break;
  365. case Key.Down:
  366. movement = MoveVertical(1, modifiers);
  367. break;
  368. case Key.Home:
  369. MoveHome(modifiers);
  370. movement = true;
  371. break;
  372. case Key.End:
  373. MoveEnd(modifiers);
  374. movement = true;
  375. break;
  376. case Key.Back:
  377. if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd)
  378. {
  379. SetSelectionForControlBackspace(modifiers);
  380. }
  381. if (!DeleteSelection() && CaretIndex > 0)
  382. {
  383. var removedCharacters = 1;
  384. // handle deleting /r/n
  385. // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
  386. // a /r should also be deleted.
  387. if (CaretIndex > 1 &&
  388. text[CaretIndex - 1] == '\n' &&
  389. text[CaretIndex - 2] == '\r')
  390. {
  391. removedCharacters = 2;
  392. }
  393. SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex));
  394. CaretIndex -= removedCharacters;
  395. SelectionStart = SelectionEnd = CaretIndex;
  396. }
  397. handled = true;
  398. break;
  399. case Key.Delete:
  400. if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd)
  401. {
  402. SetSelectionForControlDelete(modifiers);
  403. }
  404. if (!DeleteSelection() && caretIndex < text.Length)
  405. {
  406. var removedCharacters = 1;
  407. // handle deleting /r/n
  408. // you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
  409. // a /r should also be deleted.
  410. if (CaretIndex < text.Length - 1 &&
  411. text[caretIndex + 1] == '\n' &&
  412. text[caretIndex] == '\r')
  413. {
  414. removedCharacters = 2;
  415. }
  416. SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters));
  417. }
  418. handled = true;
  419. break;
  420. case Key.Enter:
  421. if (AcceptsReturn)
  422. {
  423. HandleTextInput("\r\n");
  424. handled = true;
  425. }
  426. break;
  427. case Key.Tab:
  428. if (AcceptsTab)
  429. {
  430. HandleTextInput("\t");
  431. handled = true;
  432. }
  433. else
  434. {
  435. base.OnKeyDown(e);
  436. }
  437. break;
  438. default:
  439. handled = false;
  440. break;
  441. }
  442. if (movement && ((modifiers & InputModifiers.Shift) != 0))
  443. {
  444. SelectionEnd = CaretIndex;
  445. }
  446. else if (movement)
  447. {
  448. SelectionStart = SelectionEnd = CaretIndex;
  449. }
  450. if (handled || movement)
  451. {
  452. e.Handled = true;
  453. }
  454. }
  455. protected override void OnPointerPressed(PointerPressedEventArgs e)
  456. {
  457. var point = e.GetPosition(_presenter);
  458. var index = CaretIndex = _presenter.GetCaretIndex(point);
  459. var text = Text;
  460. if (text != null)
  461. {
  462. switch (e.ClickCount)
  463. {
  464. case 1:
  465. SelectionStart = SelectionEnd = index;
  466. break;
  467. case 2:
  468. if (!StringUtils.IsStartOfWord(text, index))
  469. {
  470. SelectionStart = StringUtils.PreviousWord(text, index);
  471. }
  472. SelectionEnd = StringUtils.NextWord(text, index);
  473. break;
  474. case 3:
  475. SelectionStart = 0;
  476. SelectionEnd = text.Length;
  477. break;
  478. }
  479. }
  480. e.Device.Capture(_presenter);
  481. e.Handled = true;
  482. }
  483. protected override void OnPointerMoved(PointerEventArgs e)
  484. {
  485. if (_presenter != null && e.Device.Captured == _presenter)
  486. {
  487. var point = e.GetPosition(_presenter);
  488. CaretIndex = SelectionEnd = _presenter.GetCaretIndex(point);
  489. }
  490. }
  491. protected override void OnPointerReleased(PointerReleasedEventArgs e)
  492. {
  493. if (_presenter != null && e.Device.Captured == _presenter)
  494. {
  495. e.Device.Capture(null);
  496. }
  497. }
  498. protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification status)
  499. {
  500. if (property == TextProperty)
  501. {
  502. DataValidationErrors.SetError(this, status.Error);
  503. }
  504. }
  505. private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text?.Length ?? 0);
  506. private int CoerceCaretIndex(int value, int length)
  507. {
  508. var text = Text;
  509. if (value < 0)
  510. {
  511. return 0;
  512. }
  513. else if (value > length)
  514. {
  515. return length;
  516. }
  517. else if (value > 0 && text[value - 1] == '\r' && text[value] == '\n')
  518. {
  519. return value + 1;
  520. }
  521. else
  522. {
  523. return value;
  524. }
  525. }
  526. private int DeleteCharacter(int index)
  527. {
  528. var start = index + 1;
  529. var text = Text;
  530. var c = text[index];
  531. var result = 1;
  532. if (c == '\n' && index > 0 && text[index - 1] == '\r')
  533. {
  534. --index;
  535. ++result;
  536. }
  537. else if (c == '\r' && index < text.Length - 1 && text[index + 1] == '\n')
  538. {
  539. ++start;
  540. ++result;
  541. }
  542. Text = text.Substring(0, index) + text.Substring(start);
  543. return result;
  544. }
  545. private void MoveHorizontal(int direction, InputModifiers modifiers)
  546. {
  547. var text = Text ?? string.Empty;
  548. var caretIndex = CaretIndex;
  549. if ((modifiers & InputModifiers.Control) == 0)
  550. {
  551. var index = caretIndex + direction;
  552. if (index < 0 || index > text.Length)
  553. {
  554. return;
  555. }
  556. else if (index == text.Length)
  557. {
  558. CaretIndex = index;
  559. return;
  560. }
  561. var c = text[index];
  562. if (direction > 0)
  563. {
  564. CaretIndex += (c == '\r' && index < text.Length - 1 && text[index + 1] == '\n') ? 2 : 1;
  565. }
  566. else
  567. {
  568. CaretIndex -= (c == '\n' && index > 0 && text[index - 1] == '\r') ? 2 : 1;
  569. }
  570. }
  571. else
  572. {
  573. if (direction > 0)
  574. {
  575. CaretIndex += StringUtils.NextWord(text, caretIndex) - caretIndex;
  576. }
  577. else
  578. {
  579. CaretIndex += StringUtils.PreviousWord(text, caretIndex) - caretIndex;
  580. }
  581. }
  582. }
  583. private bool MoveVertical(int count, InputModifiers modifiers)
  584. {
  585. var formattedText = _presenter.FormattedText;
  586. var lines = formattedText.GetLines().ToList();
  587. var caretIndex = CaretIndex;
  588. var lineIndex = GetLine(caretIndex, lines) + count;
  589. if (lineIndex >= 0 && lineIndex < lines.Count)
  590. {
  591. var line = lines[lineIndex];
  592. var rect = formattedText.HitTestTextPosition(caretIndex);
  593. var y = count < 0 ? rect.Y : rect.Bottom;
  594. var point = new Point(rect.X, y + (count * (line.Height / 2)));
  595. var hit = formattedText.HitTestPoint(point);
  596. CaretIndex = hit.TextPosition + (hit.IsTrailing ? 1 : 0);
  597. return true;
  598. }
  599. else
  600. {
  601. return false;
  602. }
  603. }
  604. private void MoveHome(InputModifiers modifiers)
  605. {
  606. var text = Text ?? string.Empty;
  607. var caretIndex = CaretIndex;
  608. if ((modifiers & InputModifiers.Control) != 0)
  609. {
  610. caretIndex = 0;
  611. }
  612. else
  613. {
  614. var lines = _presenter.FormattedText.GetLines();
  615. var pos = 0;
  616. foreach (var line in lines)
  617. {
  618. if (pos + line.Length > caretIndex || pos + line.Length == text.Length)
  619. {
  620. break;
  621. }
  622. pos += line.Length;
  623. }
  624. caretIndex = pos;
  625. }
  626. CaretIndex = caretIndex;
  627. }
  628. private void MoveEnd(InputModifiers modifiers)
  629. {
  630. var text = Text ?? string.Empty;
  631. var caretIndex = CaretIndex;
  632. if ((modifiers & InputModifiers.Control) != 0)
  633. {
  634. caretIndex = text.Length;
  635. }
  636. else
  637. {
  638. var lines = _presenter.FormattedText.GetLines();
  639. var pos = 0;
  640. foreach (var line in lines)
  641. {
  642. pos += line.Length;
  643. if (pos > caretIndex)
  644. {
  645. if (pos < text.Length)
  646. {
  647. --pos;
  648. if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n')
  649. {
  650. --pos;
  651. }
  652. }
  653. break;
  654. }
  655. }
  656. caretIndex = pos;
  657. }
  658. CaretIndex = caretIndex;
  659. }
  660. private void SelectAll()
  661. {
  662. SelectionStart = 0;
  663. SelectionEnd = Text?.Length ?? 0;
  664. }
  665. private bool DeleteSelection()
  666. {
  667. if (!IsReadOnly)
  668. {
  669. var selectionStart = SelectionStart;
  670. var selectionEnd = SelectionEnd;
  671. if (selectionStart != selectionEnd)
  672. {
  673. var start = Math.Min(selectionStart, selectionEnd);
  674. var end = Math.Max(selectionStart, selectionEnd);
  675. var text = Text;
  676. SetTextInternal(text.Substring(0, start) + text.Substring(end));
  677. SelectionStart = SelectionEnd = CaretIndex = start;
  678. return true;
  679. }
  680. else
  681. {
  682. return false;
  683. }
  684. }
  685. else
  686. {
  687. return true;
  688. }
  689. }
  690. private string GetSelection()
  691. {
  692. var text = Text;
  693. if (string.IsNullOrEmpty(text))
  694. return "";
  695. var selectionStart = SelectionStart;
  696. var selectionEnd = SelectionEnd;
  697. var start = Math.Min(selectionStart, selectionEnd);
  698. var end = Math.Max(selectionStart, selectionEnd);
  699. if (start == end || (Text?.Length ?? 0) < end)
  700. {
  701. return "";
  702. }
  703. return text.Substring(start, end - start);
  704. }
  705. private int GetLine(int caretIndex, IList<FormattedTextLine> lines)
  706. {
  707. int pos = 0;
  708. int i;
  709. for (i = 0; i < lines.Count - 1; ++i)
  710. {
  711. var line = lines[i];
  712. pos += line.Length;
  713. if (pos > caretIndex)
  714. {
  715. break;
  716. }
  717. }
  718. return i;
  719. }
  720. private void SetTextInternal(string value)
  721. {
  722. try
  723. {
  724. _ignoreTextChanges = true;
  725. SetAndRaise(TextProperty, ref _text, value);
  726. }
  727. finally
  728. {
  729. _ignoreTextChanges = false;
  730. }
  731. }
  732. private void SetSelectionForControlBackspace(InputModifiers modifiers)
  733. {
  734. SelectionStart = CaretIndex;
  735. MoveHorizontal(-1, modifiers);
  736. SelectionEnd = CaretIndex;
  737. }
  738. private void SetSelectionForControlDelete(InputModifiers modifiers)
  739. {
  740. SelectionStart = CaretIndex;
  741. MoveHorizontal(1, modifiers);
  742. SelectionEnd = CaretIndex;
  743. }
  744. UndoRedoState UndoRedoHelper<UndoRedoState>.IUndoRedoHost.UndoRedoState
  745. {
  746. get { return new UndoRedoState(Text, CaretIndex); }
  747. set
  748. {
  749. Text = value.Text;
  750. SelectionStart = SelectionEnd = CaretIndex = value.CaretPosition;
  751. }
  752. }
  753. }
  754. }