TextBoxTests.cs 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reactive.Linq;
  4. using System.Threading.Tasks;
  5. using Avalonia.Controls.Presenters;
  6. using Avalonia.Controls.Primitives;
  7. using Avalonia.Controls.Templates;
  8. using Avalonia.Data;
  9. using Avalonia.Headless;
  10. using Avalonia.Input;
  11. using Avalonia.Input.Platform;
  12. using Avalonia.Input.TextInput;
  13. using Avalonia.Layout;
  14. using Avalonia.Media;
  15. using Avalonia.Platform;
  16. using Avalonia.UnitTests;
  17. using Avalonia.VisualTree;
  18. using Moq;
  19. using Xunit;
  20. namespace Avalonia.Controls.UnitTests
  21. {
  22. public class TextBoxTests
  23. {
  24. [Fact]
  25. public void Opening_Context_Menu_Does_not_Lose_Selection()
  26. {
  27. using (UnitTestApplication.Start(FocusServices))
  28. {
  29. var target1 = new TextBox
  30. {
  31. Template = CreateTemplate(),
  32. Text = "1234",
  33. ContextMenu = new TestContextMenu()
  34. };
  35. var target2 = new TextBox
  36. {
  37. Template = CreateTemplate(),
  38. Text = "5678"
  39. };
  40. var sp = new StackPanel();
  41. sp.Children.Add(target1);
  42. sp.Children.Add(target2);
  43. target1.ApplyTemplate();
  44. target2.ApplyTemplate();
  45. var root = new TestRoot() { Child = sp };
  46. target1.SelectionStart = 0;
  47. target1.SelectionEnd = 3;
  48. target1.Focus();
  49. Assert.False(target2.IsFocused);
  50. Assert.True(target1.IsFocused);
  51. target2.Focus();
  52. Assert.Equal("123", target1.SelectedText);
  53. }
  54. }
  55. [Fact]
  56. public void TextBox_Should_Lose_Focus_When_Disabled()
  57. {
  58. using (UnitTestApplication.Start(FocusServices))
  59. {
  60. var target = new TextBox
  61. {
  62. Template = CreateTemplate()
  63. };
  64. target.ApplyTemplate();
  65. var root = new TestRoot() { Child = target };
  66. target.Focus();
  67. Assert.True(target.IsFocused);
  68. target.IsEnabled = false;
  69. Assert.False(target.IsFocused);
  70. Assert.False(target.IsEnabled);
  71. }
  72. }
  73. [Fact]
  74. public void Opening_Context_Flyout_Does_not_Lose_Selection()
  75. {
  76. using (UnitTestApplication.Start(FocusServices))
  77. {
  78. var target1 = new TextBox
  79. {
  80. Template = CreateTemplate(),
  81. Text = "1234",
  82. ContextFlyout = new MenuFlyout
  83. {
  84. Items =
  85. {
  86. new MenuItem { Header = "Item 1" },
  87. new MenuItem {Header = "Item 2" },
  88. new MenuItem {Header = "Item 3" }
  89. }
  90. }
  91. };
  92. target1.ApplyTemplate();
  93. var root = new TestRoot() { Child = target1 };
  94. target1.SelectionStart = 0;
  95. target1.SelectionEnd = 3;
  96. target1.Focus();
  97. Assert.True(target1.IsFocused);
  98. target1.ContextFlyout.ShowAt(target1);
  99. Assert.Equal("123", target1.SelectedText);
  100. }
  101. }
  102. [Fact]
  103. public void DefaultBindingMode_Should_Be_TwoWay()
  104. {
  105. Assert.Equal(
  106. BindingMode.TwoWay,
  107. TextBox.TextProperty.GetMetadata(typeof(TextBox)).DefaultBindingMode);
  108. }
  109. [Fact]
  110. public void TextBox_Ignore_Word_Move_In_Password_Field()
  111. {
  112. using (UnitTestApplication.Start(Services))
  113. {
  114. var target = new TextBox
  115. {
  116. Template = CreateTemplate(),
  117. PasswordChar = '*',
  118. Text = "passw0rd"
  119. };
  120. target.ApplyTemplate();
  121. target.Measure(Size.Infinity);
  122. target.CaretIndex = 8;
  123. RaiseKeyEvent(target, Key.Left, KeyModifiers.Control);
  124. Assert.Equal(7, target.CaretIndex);
  125. }
  126. }
  127. [Fact]
  128. public void CaretIndex_Can_Moved_To_Position_After_The_End_Of_Text_With_Arrow_Key()
  129. {
  130. using (UnitTestApplication.Start(Services))
  131. {
  132. var target = new TextBox
  133. {
  134. Template = CreateTemplate(),
  135. Text = "1234"
  136. };
  137. target.ApplyTemplate();
  138. target.Measure(Size.Infinity);
  139. target.CaretIndex = 3;
  140. RaiseKeyEvent(target, Key.Right, 0);
  141. Assert.Equal(4, target.CaretIndex);
  142. }
  143. }
  144. [Fact]
  145. public void Control_Backspace_Should_Set_Caret_Position_To_The_Start_Of_The_Deletion()
  146. {
  147. using (UnitTestApplication.Start(Services))
  148. {
  149. var target = new TextBox
  150. {
  151. Template = CreateTemplate(),
  152. Text = "First Second Third",
  153. SelectionStart = 13,
  154. SelectionEnd = 13
  155. };
  156. target.CaretIndex = 10;
  157. target.ApplyTemplate();
  158. // (First Second |Third)
  159. RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
  160. // (First |Third)
  161. Assert.Equal(6, target.CaretIndex);
  162. }
  163. }
  164. [Fact]
  165. public void Control_Backspace_Should_Remove_The_Double_Whitespace_If_Caret_Index_Was_At_The_End_Of_A_Word()
  166. {
  167. using (UnitTestApplication.Start(Services))
  168. {
  169. var target = new TextBox
  170. {
  171. Template = CreateTemplate(),
  172. Text = "First Second Third",
  173. SelectionStart = 12,
  174. SelectionEnd = 12
  175. };
  176. target.ApplyTemplate();
  177. // (First Second| Third)
  178. RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
  179. // (First| Third)
  180. Assert.Equal("First Third", target.Text);
  181. }
  182. }
  183. [Fact]
  184. public void Control_Backspace_Undo_Should_Return_Caret_Position()
  185. {
  186. using (UnitTestApplication.Start(Services))
  187. {
  188. var target = new TextBox
  189. {
  190. Template = CreateTemplate(),
  191. Text = "First Second Third",
  192. SelectionStart = 9,
  193. SelectionEnd = 9
  194. };
  195. target.ApplyTemplate();
  196. // (First Second| Third)
  197. RaiseKeyEvent(target, Key.Back, KeyModifiers.Control);
  198. // (First| Third)
  199. target.Undo();
  200. // (First Second| Third)
  201. Assert.Equal(9, target.CaretIndex);
  202. }
  203. }
  204. [Fact]
  205. public void Press_Ctrl_A_Select_All_Text()
  206. {
  207. using (UnitTestApplication.Start(Services))
  208. {
  209. var target = new TextBox
  210. {
  211. Template = CreateTemplate(),
  212. Text = "1234"
  213. };
  214. target.ApplyTemplate();
  215. RaiseKeyEvent(target, Key.A, KeyModifiers.Control);
  216. Assert.Equal(0, target.SelectionStart);
  217. Assert.Equal(4, target.SelectionEnd);
  218. }
  219. }
  220. [Fact]
  221. public void Press_Ctrl_A_Select_All_Null_Text()
  222. {
  223. using (UnitTestApplication.Start(Services))
  224. {
  225. var target = new TextBox
  226. {
  227. Template = CreateTemplate()
  228. };
  229. RaiseKeyEvent(target, Key.A, KeyModifiers.Control);
  230. Assert.Equal(0, target.SelectionStart);
  231. Assert.Equal(0, target.SelectionEnd);
  232. }
  233. }
  234. [Fact]
  235. public void Press_Ctrl_Z_Will_Not_Modify_Text()
  236. {
  237. using (UnitTestApplication.Start(Services))
  238. {
  239. var target = new TextBox
  240. {
  241. Template = CreateTemplate(),
  242. Text = "1234"
  243. };
  244. RaiseKeyEvent(target, Key.Z, KeyModifiers.Control);
  245. Assert.Equal("1234", target.Text);
  246. }
  247. }
  248. [Fact]
  249. public void Control_Backspace_Should_Remove_The_Word_Before_The_Caret_If_There_Is_No_Selection()
  250. {
  251. using (UnitTestApplication.Start(Services))
  252. {
  253. TextBox textBox = new TextBox
  254. {
  255. Template = CreateTemplate(),
  256. Text = "First Second Third Fourth",
  257. SelectionStart = 5,
  258. SelectionEnd = 5
  259. };
  260. textBox.ApplyTemplate();
  261. // (First| Second Third Fourth)
  262. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  263. Assert.Equal(" Second Third Fourth", textBox.Text);
  264. // ( Second |Third Fourth)
  265. textBox.CaretIndex = 8;
  266. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  267. Assert.Equal(" Third Fourth", textBox.Text);
  268. // ( Thi|rd Fourth)
  269. textBox.CaretIndex = 4;
  270. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  271. Assert.Equal(" rd Fourth", textBox.Text);
  272. // ( rd F[ou]rth)
  273. textBox.SelectionStart = 5;
  274. textBox.SelectionEnd = 7;
  275. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  276. Assert.Equal(" rd Frth", textBox.Text);
  277. // ( |rd Frth)
  278. textBox.CaretIndex = 1;
  279. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.Control);
  280. Assert.Equal("rd Frth", textBox.Text);
  281. }
  282. }
  283. [Fact]
  284. public void Control_Delete_Should_Remove_The_Word_After_The_Caret_If_There_Is_No_Selection()
  285. {
  286. using (UnitTestApplication.Start(Services))
  287. {
  288. TextBox textBox = new TextBox
  289. {
  290. Template = CreateTemplate(),
  291. Text = "First Second Third Fourth",
  292. CaretIndex = 19,
  293. };
  294. textBox.ApplyTemplate();
  295. // (First Second Third |Fourth)
  296. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  297. Assert.Equal("First Second Third ", textBox.Text);
  298. // (First Second |Third )
  299. textBox.CaretIndex = 13;
  300. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  301. Assert.Equal("First Second ", textBox.Text);
  302. // (First Sec|ond )
  303. textBox.CaretIndex = 9;
  304. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  305. Assert.Equal("First Sec", textBox.Text);
  306. // (Fi[rs]t Sec )
  307. textBox.SelectionStart = 2;
  308. textBox.SelectionEnd = 4;
  309. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  310. Assert.Equal("Fit Sec", textBox.Text);
  311. // (Fit Sec| )
  312. textBox.Text += " ";
  313. textBox.CaretIndex = 7;
  314. RaiseKeyEvent(textBox, Key.Delete, KeyModifiers.Control);
  315. Assert.Equal("Fit Sec", textBox.Text);
  316. }
  317. }
  318. [Fact]
  319. public void Setting_SelectionStart_To_SelectionEnd_Sets_CaretPosition_To_SelectionStart()
  320. {
  321. using (UnitTestApplication.Start(Services))
  322. {
  323. var textBox = new TextBox
  324. {
  325. Text = "0123456789"
  326. };
  327. textBox.SelectionStart = 2;
  328. textBox.SelectionEnd = 2;
  329. Assert.Equal(2, textBox.CaretIndex);
  330. }
  331. }
  332. [Fact]
  333. public void Setting_Text_Updates_CaretPosition()
  334. {
  335. using (UnitTestApplication.Start(Services))
  336. {
  337. var target = new TextBox
  338. {
  339. Text = "Initial Text",
  340. CaretIndex = 11
  341. };
  342. var invoked = false;
  343. target.GetObservable(TextBox.TextProperty).Skip(1).Subscribe(_ =>
  344. {
  345. // Caret index should be set before Text changed notification, as we don't want
  346. // to notify with an invalid CaretIndex.
  347. Assert.Equal(7, target.CaretIndex);
  348. invoked = true;
  349. });
  350. target.Text = "Changed";
  351. Assert.True(invoked);
  352. }
  353. }
  354. [Fact]
  355. public void Press_Enter_Does_Not_Accept_Return()
  356. {
  357. using (UnitTestApplication.Start(Services))
  358. {
  359. var target = new TextBox
  360. {
  361. Template = CreateTemplate(),
  362. AcceptsReturn = false,
  363. Text = "1234"
  364. };
  365. target.ApplyTemplate();
  366. RaiseKeyEvent(target, Key.Enter, 0);
  367. Assert.Equal("1234", target.Text);
  368. }
  369. }
  370. [Fact]
  371. public void Press_Enter_Add_Default_Newline()
  372. {
  373. using (UnitTestApplication.Start(Services))
  374. {
  375. var target = new TextBox
  376. {
  377. Template = CreateTemplate(),
  378. AcceptsReturn = true
  379. };
  380. target.ApplyTemplate();
  381. RaiseKeyEvent(target, Key.Enter, 0);
  382. Assert.Equal(Environment.NewLine, target.Text);
  383. }
  384. }
  385. [Fact]
  386. public void Press_Enter_Add_Custom_Newline()
  387. {
  388. using (UnitTestApplication.Start(Services))
  389. {
  390. var target = new TextBox
  391. {
  392. Template = CreateTemplate(),
  393. AcceptsReturn = true,
  394. NewLine = "Test"
  395. };
  396. target.ApplyTemplate();
  397. RaiseKeyEvent(target, Key.Enter, 0);
  398. Assert.Equal("Test", target.Text);
  399. }
  400. }
  401. [Theory]
  402. [InlineData(new object[] { false, TextWrapping.NoWrap, ScrollBarVisibility.Hidden })]
  403. [InlineData(new object[] { false, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
  404. [InlineData(new object[] { true, TextWrapping.NoWrap, ScrollBarVisibility.Auto })]
  405. [InlineData(new object[] { true, TextWrapping.Wrap, ScrollBarVisibility.Disabled })]
  406. public void Has_Correct_Horizontal_ScrollBar_Visibility(
  407. bool acceptsReturn,
  408. TextWrapping wrapping,
  409. ScrollBarVisibility expected)
  410. {
  411. using (UnitTestApplication.Start(Services))
  412. {
  413. var target = new TextBox
  414. {
  415. AcceptsReturn = acceptsReturn,
  416. TextWrapping = wrapping,
  417. };
  418. Assert.Equal(expected, ScrollViewer.GetHorizontalScrollBarVisibility(target));
  419. }
  420. }
  421. [Fact]
  422. public void SelectionEnd_Doesnt_Cause_Exception()
  423. {
  424. using (UnitTestApplication.Start(Services))
  425. {
  426. var target = new TextBox
  427. {
  428. Template = CreateTemplate(),
  429. Text = "0123456789"
  430. };
  431. target.ApplyTemplate();
  432. target.SelectionStart = 0;
  433. target.SelectionEnd = 9;
  434. target.Text = "123";
  435. RaiseTextEvent(target, "456");
  436. Assert.True(true);
  437. }
  438. }
  439. [Fact]
  440. public void SelectionStart_Doesnt_Cause_Exception()
  441. {
  442. using (UnitTestApplication.Start(Services))
  443. {
  444. var target = new TextBox
  445. {
  446. Template = CreateTemplate(),
  447. Text = "0123456789"
  448. };
  449. target.ApplyTemplate();
  450. target.SelectionStart = 8;
  451. target.SelectionEnd = 9;
  452. target.Text = "123";
  453. RaiseTextEvent(target, "456");
  454. Assert.True(true);
  455. }
  456. }
  457. [Fact]
  458. public void SelectionStartEnd_Are_Valid_AterTextChange()
  459. {
  460. using (UnitTestApplication.Start(Services))
  461. {
  462. var target = new TextBox
  463. {
  464. Template = CreateTemplate(),
  465. Text = "0123456789"
  466. };
  467. target.SelectionStart = 8;
  468. target.SelectionEnd = 9;
  469. target.Text = "123";
  470. Assert.True(target.SelectionStart <= "123".Length);
  471. Assert.True(target.SelectionEnd <= "123".Length);
  472. }
  473. }
  474. [Fact]
  475. public void SelectedText_Changes_OnSelectionChange()
  476. {
  477. using (UnitTestApplication.Start(Services))
  478. {
  479. var target = new TextBox
  480. {
  481. Template = CreateTemplate(),
  482. Text = "0123456789"
  483. };
  484. target.ApplyTemplate();
  485. Assert.True(target.SelectedText == "");
  486. target.SelectionStart = 2;
  487. target.SelectionEnd = 4;
  488. Assert.True(target.SelectedText == "23");
  489. }
  490. }
  491. [Fact]
  492. public void SelectedText_EditsText()
  493. {
  494. using (UnitTestApplication.Start(Services))
  495. {
  496. var target = new TextBox
  497. {
  498. Template = CreateTemplate(),
  499. Text = "0123"
  500. };
  501. target.ApplyTemplate();
  502. target.SelectedText = "AA";
  503. Assert.True(target.Text == "AA0123");
  504. target.SelectionStart = 1;
  505. target.SelectionEnd = 3;
  506. target.SelectedText = "BB";
  507. Assert.True(target.Text == "ABB123");
  508. }
  509. }
  510. [Fact]
  511. public void SelectedText_CanClearText()
  512. {
  513. using (UnitTestApplication.Start(Services))
  514. {
  515. var target = new TextBox
  516. {
  517. Template = CreateTemplate(),
  518. Text = "0123"
  519. };
  520. target.SelectionStart = 1;
  521. target.SelectionEnd = 3;
  522. target.SelectedText = "";
  523. Assert.True(target.Text == "03");
  524. }
  525. }
  526. [Fact]
  527. public void SelectedText_NullClearsText()
  528. {
  529. using (UnitTestApplication.Start(Services))
  530. {
  531. var target = new TextBox
  532. {
  533. Template = CreateTemplate(),
  534. Text = "0123"
  535. };
  536. target.SelectionStart = 1;
  537. target.SelectionEnd = 3;
  538. target.SelectedText = null;
  539. Assert.True(target.Text == "03");
  540. }
  541. }
  542. [Fact]
  543. public void CoerceCaretIndex_Doesnt_Cause_Exception_with_malformed_line_ending()
  544. {
  545. using (UnitTestApplication.Start(Services))
  546. {
  547. var target = new TextBox
  548. {
  549. Template = CreateTemplate(),
  550. Text = "0123456789\r"
  551. };
  552. target.CaretIndex = 11;
  553. Assert.True(true);
  554. }
  555. }
  556. [Theory]
  557. [InlineData(Key.Up)]
  558. [InlineData(Key.Down)]
  559. [InlineData(Key.Home)]
  560. [InlineData(Key.End)]
  561. public void Textbox_doesnt_crash_when_Receives_input_and_template_not_applied(Key key)
  562. {
  563. using (UnitTestApplication.Start(FocusServices))
  564. {
  565. var target1 = new TextBox
  566. {
  567. Template = CreateTemplate(),
  568. Text = "1234",
  569. };
  570. var root = new TestRoot { Child = target1 };
  571. target1.Focus();
  572. Assert.True(target1.IsFocused);
  573. RaiseKeyEvent(target1, key, KeyModifiers.None);
  574. }
  575. }
  576. [Fact]
  577. public void TextBox_GotFocus_And_LostFocus_Work_Properly()
  578. {
  579. using (UnitTestApplication.Start(FocusServices))
  580. {
  581. var target1 = new TextBox
  582. {
  583. Template = CreateTemplate(),
  584. Text = "1234"
  585. };
  586. var target2 = new TextBox
  587. {
  588. Template = CreateTemplate(),
  589. Text = "5678"
  590. };
  591. var sp = new StackPanel();
  592. sp.Children.Add(target1);
  593. sp.Children.Add(target2);
  594. target1.ApplyTemplate();
  595. target2.ApplyTemplate();
  596. var root = new TestRoot { Child = sp };
  597. var gfcount = 0;
  598. var lfcount = 0;
  599. target1.GotFocus += (s, e) => gfcount++;
  600. target2.LostFocus += (s, e) => lfcount++;
  601. target2.Focus();
  602. Assert.False(target1.IsFocused);
  603. Assert.True(target2.IsFocused);
  604. target1.Focus();
  605. Assert.False(target2.IsFocused);
  606. Assert.True(target1.IsFocused);
  607. Assert.Equal(1, gfcount);
  608. Assert.Equal(1, lfcount);
  609. }
  610. }
  611. [Fact]
  612. public void TextBox_CaretIndex_Persists_When_Focus_Lost()
  613. {
  614. using (UnitTestApplication.Start(FocusServices))
  615. {
  616. var target1 = new TextBox
  617. {
  618. Template = CreateTemplate(),
  619. Text = "1234"
  620. };
  621. var target2 = new TextBox
  622. {
  623. Template = CreateTemplate(),
  624. Text = "5678"
  625. };
  626. var sp = new StackPanel();
  627. sp.Children.Add(target1);
  628. sp.Children.Add(target2);
  629. target1.ApplyTemplate();
  630. target2.ApplyTemplate();
  631. var root = new TestRoot { Child = sp };
  632. target2.Focus();
  633. target2.CaretIndex = 2;
  634. Assert.False(target1.IsFocused);
  635. Assert.True(target2.IsFocused);
  636. target1.Focus();
  637. Assert.Equal(2, target2.CaretIndex);
  638. }
  639. }
  640. [Fact]
  641. public void TextBox_Reveal_Password_Reset_When_Lost_Focus()
  642. {
  643. using (UnitTestApplication.Start(FocusServices))
  644. {
  645. var target1 = new TextBox
  646. {
  647. Template = CreateTemplate(),
  648. Text = "1234",
  649. PasswordChar = '*'
  650. };
  651. var target2 = new TextBox
  652. {
  653. Template = CreateTemplate(),
  654. Text = "5678"
  655. };
  656. var sp = new StackPanel();
  657. sp.Children.Add(target1);
  658. sp.Children.Add(target2);
  659. target1.ApplyTemplate();
  660. target2.ApplyTemplate();
  661. var root = new TestRoot { Child = sp };
  662. target1.Focus();
  663. target1.RevealPassword = true;
  664. target2.Focus();
  665. Assert.False(target1.RevealPassword);
  666. }
  667. }
  668. [Fact]
  669. public void Setting_Bound_Text_To_Null_Works()
  670. {
  671. using (UnitTestApplication.Start(Services))
  672. {
  673. var source = new Class1 { Bar = "bar" };
  674. var target = new TextBox { Template = CreateTemplate(), DataContext = source };
  675. target.ApplyTemplate();
  676. target.Bind(TextBox.TextProperty, new Binding("Bar"));
  677. Assert.Equal("bar", target.Text);
  678. source.Bar = null;
  679. Assert.Null(target.Text);
  680. }
  681. }
  682. [Theory]
  683. [InlineData("abc", "d", 3, 0, 0, false, "abc")]
  684. [InlineData("abc", "dd", 4, 3, 3, false, "abcd")]
  685. [InlineData("abc", "ddd", 3, 0, 2, true, "ddc")]
  686. [InlineData("abc", "dddd", 4, 1, 3, true, "addd")]
  687. [InlineData("abc", "ddddd", 5, 3, 3, true, "abcdd")]
  688. public void MaxLength_Works_Properly(
  689. string initalText,
  690. string textInput,
  691. int maxLength,
  692. int selectionStart,
  693. int selectionEnd,
  694. bool fromClipboard,
  695. string expected)
  696. {
  697. using (UnitTestApplication.Start(Services))
  698. {
  699. var target = new TextBox
  700. {
  701. Template = CreateTemplate(),
  702. Text = initalText,
  703. MaxLength = maxLength,
  704. SelectionStart = selectionStart,
  705. SelectionEnd = selectionEnd
  706. };
  707. var impl = CreateMockTopLevelImpl();
  708. var topLevel = new TestTopLevel(impl.Object)
  709. {
  710. Template = CreateTopLevelTemplate()
  711. };
  712. topLevel.Content = target;
  713. topLevel.ApplyTemplate();
  714. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  715. target.Measure(Size.Infinity);
  716. if (fromClipboard)
  717. {
  718. topLevel.Clipboard?.SetTextAsync(textInput).GetAwaiter().GetResult();
  719. RaiseKeyEvent(target, Key.V, KeyModifiers.Control);
  720. topLevel.Clipboard?.ClearAsync().GetAwaiter().GetResult();
  721. }
  722. else
  723. {
  724. RaiseTextEvent(target, textInput);
  725. }
  726. Assert.Equal(expected, target.Text);
  727. }
  728. }
  729. [Theory]
  730. [InlineData(Key.X, KeyModifiers.Control)]
  731. [InlineData(Key.Back, KeyModifiers.None)]
  732. [InlineData(Key.Delete, KeyModifiers.None)]
  733. [InlineData(Key.Tab, KeyModifiers.None)]
  734. [InlineData(Key.Enter, KeyModifiers.None)]
  735. public void Keys_Allow_Undo(Key key, KeyModifiers modifiers)
  736. {
  737. using (UnitTestApplication.Start(Services))
  738. {
  739. var target = new TextBox
  740. {
  741. Template = CreateTemplate(),
  742. Text = "0123",
  743. AcceptsReturn = true,
  744. AcceptsTab = true
  745. };
  746. var impl = CreateMockTopLevelImpl();
  747. var topLevel = new TestTopLevel(impl.Object)
  748. {
  749. Template = CreateTopLevelTemplate()
  750. };
  751. topLevel.Content = target;
  752. topLevel.ApplyTemplate();
  753. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  754. target.ApplyTemplate();
  755. target.SelectionStart = 1;
  756. target.SelectionEnd = 3;
  757. RaiseKeyEvent(target, key, modifiers);
  758. RaiseKeyEvent(target, Key.Z, KeyModifiers.Control); // undo
  759. Assert.True(target.Text == "0123");
  760. }
  761. }
  762. [Fact]
  763. public void Setting_SelectedText_Should_Fire_Single_Text_Changed_Notification()
  764. {
  765. using (UnitTestApplication.Start(Services))
  766. {
  767. var target = new TextBox
  768. {
  769. Template = CreateTemplate(),
  770. Text = "0123",
  771. AcceptsReturn = true,
  772. AcceptsTab = true,
  773. SelectionStart = 1,
  774. SelectionEnd = 3,
  775. };
  776. var values = new List<string>();
  777. target.GetObservable(TextBox.TextProperty).Subscribe(x => values.Add(x));
  778. target.SelectedText = "A";
  779. Assert.Equal(new[] { "0123", "0A3" }, values);
  780. }
  781. }
  782. [Fact]
  783. public void Entering_Text_With_SelectedText_Should_Fire_Single_Text_Changed_Notification()
  784. {
  785. using (UnitTestApplication.Start(Services))
  786. {
  787. var target = new TextBox
  788. {
  789. Template = CreateTemplate(),
  790. Text = "0123",
  791. AcceptsReturn = true,
  792. AcceptsTab = true,
  793. SelectionStart = 1,
  794. SelectionEnd = 3,
  795. };
  796. var values = new List<string>();
  797. target.GetObservable(TextBox.TextProperty).Subscribe(x => values.Add(x));
  798. RaiseTextEvent(target, "A");
  799. Assert.Equal(new[] { "0123", "0A3" }, values);
  800. }
  801. }
  802. [Fact]
  803. public void Insert_Multiline_Text_Should_Accept_Extra_Lines_When_AcceptsReturn_Is_True()
  804. {
  805. using (UnitTestApplication.Start(Services))
  806. {
  807. var target = new TextBox
  808. {
  809. AcceptsReturn = true
  810. };
  811. RaiseTextEvent(target, $"123 {Environment.NewLine}456");
  812. Assert.Equal($"123 {Environment.NewLine}456", target.Text);
  813. }
  814. }
  815. [Fact]
  816. public void Insert_Multiline_Text_Should_Discard_Extra_Lines_When_AcceptsReturn_Is_False()
  817. {
  818. using (UnitTestApplication.Start(Services))
  819. {
  820. var target = new TextBox
  821. {
  822. AcceptsReturn = false
  823. };
  824. RaiseTextEvent(target, $"123 {"\r"}456");
  825. Assert.Equal("123 ", target.Text);
  826. target.Text = "";
  827. RaiseTextEvent(target, $"123 {"\r\n"}456");
  828. Assert.Equal("123 ", target.Text);
  829. }
  830. }
  831. [Fact]
  832. public void Should_Fullfill_MaxLines_Contraint()
  833. {
  834. using (UnitTestApplication.Start(Services))
  835. {
  836. var target = new TextBox
  837. {
  838. Template = CreateTemplate(),
  839. Text = "ABC",
  840. MaxLines = 1,
  841. AcceptsReturn = true
  842. };
  843. var impl = CreateMockTopLevelImpl();
  844. var topLevel = new TestTopLevel(impl.Object)
  845. {
  846. Template = CreateTopLevelTemplate()
  847. };
  848. topLevel.Content = target;
  849. topLevel.ApplyTemplate();
  850. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  851. target.ApplyTemplate();
  852. target.Measure(Size.Infinity);
  853. var initialHeight = target.DesiredSize.Height;
  854. topLevel.Clipboard?.SetTextAsync(Environment.NewLine).GetAwaiter().GetResult();
  855. RaiseKeyEvent(target, Key.V, KeyModifiers.Control);
  856. topLevel.Clipboard?.ClearAsync().GetAwaiter().GetResult();
  857. RaiseTextEvent(target, Environment.NewLine);
  858. target.InvalidateMeasure();
  859. target.Measure(Size.Infinity);
  860. Assert.Equal(initialHeight, target.DesiredSize.Height);
  861. }
  862. }
  863. [Theory]
  864. [InlineData(1)]
  865. [InlineData(2)]
  866. [InlineData(3)]
  867. public void MaxLines_Sets_ScrollViewer_MaxHeight(int maxLines)
  868. {
  869. using (UnitTestApplication.Start(Services))
  870. {
  871. var target = new TextBox
  872. {
  873. Template = CreateTemplate(),
  874. MaxLines = maxLines,
  875. // Define explicit whole number line height for predictable calculations
  876. LineHeight = 20
  877. };
  878. var impl = CreateMockTopLevelImpl();
  879. var topLevel = new TestTopLevel(impl.Object)
  880. {
  881. Template = CreateTopLevelTemplate(),
  882. Content = target
  883. };
  884. topLevel.ApplyTemplate();
  885. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  886. var textPresenter = target.FindDescendantOfType<TextPresenter>();
  887. Assert.Equal("PART_TextPresenter", textPresenter.Name);
  888. Assert.Equal(new Thickness(0), textPresenter.Margin); // Test assumes no margin on TextPresenter
  889. var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
  890. Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
  891. Assert.Equal(maxLines * target.LineHeight, scrollViewer.MaxHeight);
  892. }
  893. }
  894. [Theory]
  895. [InlineData(1)]
  896. [InlineData(2)]
  897. [InlineData(3)]
  898. public void MaxLines_Sets_ScrollViewer_MaxHeight_With_TextPresenter_Margin(int maxLines)
  899. {
  900. using (UnitTestApplication.Start(Services))
  901. {
  902. var target = new TextBox
  903. {
  904. Template = CreateTemplate(),
  905. MaxLines = maxLines,
  906. // Define explicit whole number line height for predictable calculations
  907. LineHeight = 20
  908. };
  909. var impl = CreateMockTopLevelImpl();
  910. var topLevel = new TestTopLevel(impl.Object)
  911. {
  912. Template = CreateTopLevelTemplate(),
  913. Content = target
  914. };
  915. topLevel.ApplyTemplate();
  916. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  917. var textPresenter = target.FindDescendantOfType<TextPresenter>();
  918. Assert.Equal("PART_TextPresenter", textPresenter.Name);
  919. var textPresenterMargin = new Thickness(horizontal: 0, vertical: 3);
  920. textPresenter.Margin = textPresenterMargin;
  921. target.InvalidateMeasure();
  922. target.Measure(Size.Infinity);
  923. var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
  924. Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
  925. Assert.Equal((maxLines * target.LineHeight) + textPresenterMargin.Top + textPresenterMargin.Bottom, scrollViewer.MaxHeight);
  926. }
  927. }
  928. [Fact]
  929. public void Should_Fullfill_MinLines_Contraint()
  930. {
  931. using (UnitTestApplication.Start(Services))
  932. {
  933. var target = new TextBox
  934. {
  935. Template = CreateTemplate(),
  936. Text = "ABC \n DEF \n GHI",
  937. MinLines = 3,
  938. AcceptsReturn = true
  939. };
  940. var impl = CreateMockTopLevelImpl();
  941. var topLevel = new TestTopLevel(impl.Object)
  942. {
  943. Template = CreateTopLevelTemplate()
  944. };
  945. topLevel.Content = target;
  946. topLevel.ApplyTemplate();
  947. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  948. target.ApplyTemplate();
  949. target.Measure(Size.Infinity);
  950. var initialHeight = target.DesiredSize.Height;
  951. target.Text = "";
  952. target.InvalidateMeasure();
  953. target.Measure(Size.Infinity);
  954. Assert.Equal(initialHeight, target.DesiredSize.Height);
  955. }
  956. }
  957. [Theory]
  958. [InlineData(1)]
  959. [InlineData(2)]
  960. [InlineData(3)]
  961. public void MinLines_Sets_ScrollViewer_MinHeight(int minLines)
  962. {
  963. using (UnitTestApplication.Start(Services))
  964. {
  965. var target = new TextBox
  966. {
  967. Template = CreateTemplate(),
  968. MinLines = minLines,
  969. // Define explicit whole number line height for predictable calculations
  970. LineHeight = 20
  971. };
  972. var impl = CreateMockTopLevelImpl();
  973. var topLevel = new TestTopLevel(impl.Object)
  974. {
  975. Template = CreateTopLevelTemplate(),
  976. Content = target
  977. };
  978. topLevel.ApplyTemplate();
  979. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  980. var textPresenter = target.FindDescendantOfType<TextPresenter>();
  981. Assert.Equal("PART_TextPresenter", textPresenter.Name);
  982. Assert.Equal(new Thickness(0), textPresenter.Margin); // Test assumes no margin on TextPresenter
  983. var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
  984. Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
  985. Assert.Equal(minLines * target.LineHeight, scrollViewer.MinHeight);
  986. }
  987. }
  988. [Theory]
  989. [InlineData(1)]
  990. [InlineData(2)]
  991. [InlineData(3)]
  992. public void MinLines_Sets_ScrollViewer_MinHeight_With_TextPresenter_Margin(int minLines)
  993. {
  994. using (UnitTestApplication.Start(Services))
  995. {
  996. var target = new TextBox
  997. {
  998. Template = CreateTemplate(),
  999. MinLines = minLines,
  1000. // Define explicit whole number line height for predictable calculations
  1001. LineHeight = 20
  1002. };
  1003. var impl = CreateMockTopLevelImpl();
  1004. var topLevel = new TestTopLevel(impl.Object)
  1005. {
  1006. Template = CreateTopLevelTemplate(),
  1007. Content = target
  1008. };
  1009. topLevel.ApplyTemplate();
  1010. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  1011. var textPresenter = target.FindDescendantOfType<TextPresenter>();
  1012. Assert.Equal("PART_TextPresenter", textPresenter.Name);
  1013. var textPresenterMargin = new Thickness(horizontal: 0, vertical: 3);
  1014. textPresenter.Margin = textPresenterMargin;
  1015. target.InvalidateMeasure();
  1016. target.Measure(Size.Infinity);
  1017. var scrollViewer = target.FindDescendantOfType<ScrollViewer>();
  1018. Assert.Equal("PART_ScrollViewer", scrollViewer.Name);
  1019. Assert.Equal((minLines * target.LineHeight) + textPresenterMargin.Top + textPresenterMargin.Bottom, scrollViewer.MinHeight);
  1020. }
  1021. }
  1022. [Fact]
  1023. public void CanUndo_CanRedo_Is_False_When_Initialized()
  1024. {
  1025. using (UnitTestApplication.Start(Services))
  1026. {
  1027. var tb = new TextBox
  1028. {
  1029. Template = CreateTemplate(),
  1030. Text = "New Text"
  1031. };
  1032. tb.Measure(Size.Infinity);
  1033. Assert.False(tb.CanUndo);
  1034. Assert.False(tb.CanRedo);
  1035. }
  1036. }
  1037. [Fact]
  1038. public void CanUndo_CanRedo_and_Programmatic_Undo_Redo_Works()
  1039. {
  1040. using (UnitTestApplication.Start(Services))
  1041. {
  1042. var tb = new TextBox
  1043. {
  1044. Template = CreateTemplate(),
  1045. };
  1046. tb.Measure(Size.Infinity);
  1047. // See GH #6024 for a bit more insight on when Undo/Redo snapshots are taken:
  1048. // - Every 'Space', but only when space is handled in OnKeyDown - Spaces in TextInput event won't work
  1049. // - Every 7 chars in a long word
  1050. RaiseTextEvent(tb, "ABC");
  1051. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1052. RaiseTextEvent(tb, "DEF");
  1053. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1054. RaiseTextEvent(tb, "123");
  1055. // NOTE: the spaces won't actually add spaces b/c they're sent only as key events and not Text events
  1056. // so our final text is without spaces
  1057. Assert.Equal("ABCDEF123", tb.Text);
  1058. Assert.True(tb.CanUndo);
  1059. tb.Undo();
  1060. // Undo will take us back one step
  1061. Assert.Equal("ABCDEF", tb.Text);
  1062. Assert.True(tb.CanRedo);
  1063. tb.Redo();
  1064. // Redo should restore us
  1065. Assert.Equal("ABCDEF123", tb.Text);
  1066. }
  1067. }
  1068. [Fact]
  1069. public void Setting_UndoLimit_Clears_Undo_Redo()
  1070. {
  1071. using (UnitTestApplication.Start(Services))
  1072. {
  1073. var tb = new TextBox
  1074. {
  1075. Template = CreateTemplate(),
  1076. };
  1077. tb.Measure(Size.Infinity);
  1078. // This is all the same as the above test (CanUndo_CanRedo_and_Programmatic_Undo_Redo_Works)
  1079. // We do this to get the undo/redo stacks in a state where both are active
  1080. RaiseTextEvent(tb, "ABC");
  1081. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1082. RaiseTextEvent(tb, "DEF");
  1083. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1084. RaiseTextEvent(tb, "123");
  1085. Assert.Equal("ABCDEF123", tb.Text);
  1086. Assert.True(tb.CanUndo);
  1087. tb.Undo();
  1088. // Undo will take us back one step
  1089. Assert.Equal("ABCDEF", tb.Text);
  1090. Assert.True(tb.CanRedo);
  1091. tb.Redo();
  1092. // Redo should restore us
  1093. Assert.Equal("ABCDEF123", tb.Text);
  1094. // Change the undo limit, this should clear both stacks setting CanUndo and CanRedo to false
  1095. tb.UndoLimit = 1;
  1096. Assert.False(tb.CanUndo);
  1097. Assert.False(tb.CanRedo);
  1098. }
  1099. }
  1100. [Fact]
  1101. public void Setting_IsUndoEnabled_To_False_Clears_Undo_Redo()
  1102. {
  1103. using (UnitTestApplication.Start(Services))
  1104. {
  1105. var tb = new TextBox
  1106. {
  1107. Template = CreateTemplate(),
  1108. };
  1109. tb.Measure(Size.Infinity);
  1110. // This is all the same as the above test (CanUndo_CanRedo_and_Programmatic_Undo_Redo_Works)
  1111. // We do this to get the undo/redo stacks in a state where both are active
  1112. RaiseTextEvent(tb, "ABC");
  1113. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1114. RaiseTextEvent(tb, "DEF");
  1115. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1116. RaiseTextEvent(tb, "123");
  1117. Assert.Equal("ABCDEF123", tb.Text);
  1118. Assert.True(tb.CanUndo);
  1119. tb.Undo();
  1120. // Undo will take us back one step
  1121. Assert.Equal("ABCDEF", tb.Text);
  1122. Assert.True(tb.CanRedo);
  1123. tb.Redo();
  1124. // Redo should restore us
  1125. Assert.Equal("ABCDEF123", tb.Text);
  1126. // Disable Undo/Redo, this should clear both stacks setting CanUndo and CanRedo to false
  1127. tb.IsUndoEnabled = false;
  1128. Assert.False(tb.CanUndo);
  1129. Assert.False(tb.CanRedo);
  1130. }
  1131. }
  1132. [Fact]
  1133. public void UndoLimit_Count_Is_Respected()
  1134. {
  1135. using (UnitTestApplication.Start(Services))
  1136. {
  1137. var tb = new TextBox
  1138. {
  1139. Template = CreateTemplate(),
  1140. UndoLimit = 3 // Something small for this test
  1141. };
  1142. tb.Measure(Size.Infinity);
  1143. // Push 3 undoable actions, we should only be able to recover 2
  1144. RaiseTextEvent(tb, "ABC");
  1145. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1146. RaiseTextEvent(tb, "DEF");
  1147. RaiseKeyEvent(tb, Key.Space, KeyModifiers.None);
  1148. RaiseTextEvent(tb, "123");
  1149. Assert.Equal("ABCDEF123", tb.Text);
  1150. // Undo will take us back one step
  1151. tb.Undo();
  1152. Assert.Equal("ABCDEF", tb.Text);
  1153. // Undo again
  1154. tb.Undo();
  1155. Assert.Equal("ABC", tb.Text);
  1156. // We now should not be able to undo again
  1157. Assert.False(tb.CanUndo);
  1158. }
  1159. }
  1160. [Fact]
  1161. public void Should_Move_Caret_To_EndOfLine()
  1162. {
  1163. using (UnitTestApplication.Start(Services))
  1164. {
  1165. var tb = new TextBox
  1166. {
  1167. Template = CreateTemplate(),
  1168. Text = "AB\nAB"
  1169. };
  1170. tb.Measure(Size.Infinity);
  1171. RaiseKeyEvent(tb, Key.End, KeyModifiers.Shift);
  1172. Assert.Equal(2, tb.CaretIndex);
  1173. }
  1174. }
  1175. [Fact]
  1176. public void TextBox_In_AdornerLayer_Will_Not_Cause_Collection_Modified_In_VisualLayerManager_Measure()
  1177. {
  1178. using (UnitTestApplication.Start(Services))
  1179. {
  1180. var button = new Button();
  1181. var root = new TestRoot()
  1182. {
  1183. Child = new VisualLayerManager()
  1184. {
  1185. Child = button
  1186. }
  1187. };
  1188. var adorner = new TextBox { Template = CreateTemplate(), Text = "a" };
  1189. var adornerLayer = AdornerLayer.GetAdornerLayer(button);
  1190. adornerLayer.Children.Add(adorner);
  1191. AdornerLayer.SetAdornedElement(adorner, button);
  1192. root.Measure(Size.Infinity);
  1193. }
  1194. }
  1195. [Fact]
  1196. public void TextBox_In_AdornerLayer_Will_Not_Cause_Collection_Modified_In_VisualLayerManager_Arrange()
  1197. {
  1198. using (UnitTestApplication.Start(Services))
  1199. {
  1200. var button = new Button();
  1201. var visualLayerManager = new VisualLayerManager() { Child = button };
  1202. var root = new TestRoot()
  1203. {
  1204. Child = visualLayerManager
  1205. };
  1206. var adorner = new TextBox { Template = CreateTemplate(), Text = "a" };
  1207. var adornerLayer = AdornerLayer.GetAdornerLayer(button);
  1208. root.Measure(new Size(10, 10));
  1209. adornerLayer.Children.Add(adorner);
  1210. AdornerLayer.SetAdornedElement(adorner, button);
  1211. root.Arrange(new Rect(0, 0, 10, 10));
  1212. }
  1213. }
  1214. [Theory]
  1215. [InlineData("A\nBB\nCCC\nDDDD", 0, 0)]
  1216. [InlineData("A\nBB\nCCC\nDDDD", 1, 2)]
  1217. [InlineData("A\nBB\nCCC\nDDDD", 2, 5)]
  1218. [InlineData("A\nBB\nCCC\nDDDD", 3, 9)]
  1219. [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 0, 0)]
  1220. [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 1, 5)]
  1221. [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 2, 11)]
  1222. [InlineData("واحد\nاثنين\nثلاثة\nأربعة", 3, 17)]
  1223. public void Should_Scroll_Caret_To_Line(string text, int targetLineIndex, int expectedCaretIndex)
  1224. {
  1225. using (UnitTestApplication.Start(Services))
  1226. {
  1227. var tb = new TextBox
  1228. {
  1229. Template = CreateTemplate(),
  1230. Text = text
  1231. };
  1232. tb.ApplyTemplate();
  1233. tb.ScrollToLine(targetLineIndex);
  1234. Assert.Equal(expectedCaretIndex, tb.CaretIndex);
  1235. }
  1236. }
  1237. [Fact]
  1238. public void Should_Throw_ArgumentOutOfRange()
  1239. {
  1240. using (UnitTestApplication.Start(Services))
  1241. {
  1242. var tb = new TextBox
  1243. {
  1244. Template = CreateTemplate(),
  1245. Text = string.Empty
  1246. };
  1247. tb.ApplyTemplate();
  1248. Assert.Throws<ArgumentOutOfRangeException>(() => tb.ScrollToLine(-1));
  1249. Assert.Throws<ArgumentOutOfRangeException>(() => tb.ScrollToLine(1));
  1250. }
  1251. }
  1252. [Fact]
  1253. public void InputMethodClient_SurroundingText_Returns_Empty_For_Empty_Line()
  1254. {
  1255. using var _ = UnitTestApplication.Start(Services);
  1256. var textBox = new TextBox
  1257. {
  1258. Template = CreateTemplate(),
  1259. Text = "",
  1260. CaretIndex = 0
  1261. };
  1262. textBox.ApplyTemplate();
  1263. var eventArgs = new TextInputMethodClientRequestedEventArgs
  1264. {
  1265. RoutedEvent = InputElement.TextInputMethodClientRequestedEvent
  1266. };
  1267. textBox.RaiseEvent(eventArgs);
  1268. var client = eventArgs.Client;
  1269. Assert.NotNull(client);
  1270. Assert.Equal(string.Empty, client.SurroundingText);
  1271. }
  1272. [Fact]
  1273. public void Backspace_Should_Delete_Last_Character_In_Line_And_Keep_Caret_On_Same_Line()
  1274. {
  1275. using var _ = UnitTestApplication.Start(Services);
  1276. var textBox = new TextBox
  1277. {
  1278. Template = CreateTemplate(),
  1279. Text = "a\nb",
  1280. CaretIndex = 3
  1281. };
  1282. textBox.ApplyTemplate();
  1283. var topLevel = new TestTopLevel(CreateMockTopLevelImpl().Object)
  1284. {
  1285. Template = CreateTopLevelTemplate(),
  1286. Content = textBox
  1287. };
  1288. topLevel.ApplyTemplate();
  1289. topLevel.LayoutManager.ExecuteInitialLayoutPass();
  1290. var textPresenter = textBox.FindDescendantOfType<TextPresenter>();
  1291. Assert.NotNull(textPresenter);
  1292. var oldCaretY = textPresenter.GetCursorRectangle().Top;
  1293. Assert.NotEqual(0, oldCaretY);
  1294. RaiseKeyEvent(textBox, Key.Back, KeyModifiers.None);
  1295. Assert.Equal("a\n", textBox.Text);
  1296. Assert.Equal(2, textBox.CaretIndex);
  1297. Assert.Equal(2, textPresenter.CaretIndex);
  1298. var caretY = textPresenter.GetCursorRectangle().Top;
  1299. Assert.Equal(oldCaretY, caretY);
  1300. }
  1301. [Fact]
  1302. public void Losing_Focus_Should_Not_Reset_Selection()
  1303. {
  1304. using (UnitTestApplication.Start(FocusServices))
  1305. {
  1306. var target1 = new TextBox
  1307. {
  1308. Template = CreateTemplate(),
  1309. Text = "1234",
  1310. ClearSelectionOnLostFocus = false
  1311. };
  1312. target1.ApplyTemplate();
  1313. var target2 = new TextBox
  1314. {
  1315. Template = CreateTemplate(),
  1316. };
  1317. target2.ApplyTemplate();
  1318. var sp = new StackPanel();
  1319. sp.Children.Add(target1);
  1320. sp.Children.Add(target2);
  1321. var root = new TestRoot() { Child = sp };
  1322. target1.SelectionStart = 0;
  1323. target1.SelectionEnd = 4;
  1324. target1.Focus();
  1325. Assert.True(target1.IsFocused);
  1326. Assert.Equal("1234", target1.SelectedText);
  1327. target2.Focus();
  1328. Assert.Equal("1234", target1.SelectedText);
  1329. }
  1330. }
  1331. private static TestServices FocusServices => TestServices.MockThreadingInterface.With(
  1332. focusManager: new FocusManager(),
  1333. keyboardDevice: () => new KeyboardDevice(),
  1334. keyboardNavigation: () => new KeyboardNavigationHandler(),
  1335. inputManager: new InputManager(),
  1336. standardCursorFactory: Mock.Of<ICursorFactory>(),
  1337. textShaperImpl: new HeadlessTextShaperStub(),
  1338. fontManagerImpl: new HeadlessFontManagerStub());
  1339. private static TestServices Services => TestServices.MockThreadingInterface.With(
  1340. standardCursorFactory: Mock.Of<ICursorFactory>(),
  1341. renderInterface: new HeadlessPlatformRenderInterface(),
  1342. textShaperImpl: new HeadlessTextShaperStub(),
  1343. fontManagerImpl: new HeadlessFontManagerStub());
  1344. internal static IControlTemplate CreateTemplate()
  1345. {
  1346. return new FuncControlTemplate<TextBox>((control, scope) =>
  1347. new ScrollViewer
  1348. {
  1349. Name = "PART_ScrollViewer",
  1350. Template = new FuncControlTemplate<ScrollViewer>(ScrollViewerTests.CreateTemplate),
  1351. Content = new TextPresenter
  1352. {
  1353. Name = "PART_TextPresenter",
  1354. [!!TextPresenter.TextProperty] = new Binding
  1355. {
  1356. Path = nameof(TextPresenter.Text),
  1357. Mode = BindingMode.TwoWay,
  1358. Priority = BindingPriority.Template,
  1359. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  1360. },
  1361. [!!TextPresenter.CaretIndexProperty] = new Binding
  1362. {
  1363. Path = nameof(TextPresenter.CaretIndex),
  1364. Mode = BindingMode.TwoWay,
  1365. Priority = BindingPriority.Template,
  1366. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  1367. }
  1368. }.RegisterInNameScope(scope)
  1369. }.RegisterInNameScope(scope));
  1370. }
  1371. private static void RaiseKeyEvent(TextBox textBox, Key key, KeyModifiers inputModifiers)
  1372. {
  1373. textBox.RaiseEvent(new KeyEventArgs
  1374. {
  1375. RoutedEvent = InputElement.KeyDownEvent,
  1376. KeyModifiers = inputModifiers,
  1377. Key = key
  1378. });
  1379. }
  1380. private static void RaiseTextEvent(TextBox textBox, string text)
  1381. {
  1382. textBox.RaiseEvent(new TextInputEventArgs
  1383. {
  1384. RoutedEvent = InputElement.TextInputEvent,
  1385. Text = text
  1386. });
  1387. }
  1388. private class Class1 : NotifyingBase
  1389. {
  1390. private int _foo;
  1391. private string _bar;
  1392. public int Foo
  1393. {
  1394. get { return _foo; }
  1395. set { _foo = value; RaisePropertyChanged(); }
  1396. }
  1397. public string Bar
  1398. {
  1399. get { return _bar; }
  1400. set { _bar = value; RaisePropertyChanged(); }
  1401. }
  1402. }
  1403. private class ClipboardStub : IClipboard // in order to get tests working that use the clipboard
  1404. {
  1405. private string _text;
  1406. public Task<string> GetTextAsync() => Task.FromResult(_text);
  1407. public Task SetTextAsync(string text)
  1408. {
  1409. _text = text;
  1410. return Task.CompletedTask;
  1411. }
  1412. public Task ClearAsync()
  1413. {
  1414. _text = null;
  1415. return Task.CompletedTask;
  1416. }
  1417. public Task SetDataObjectAsync(IDataObject data) => Task.CompletedTask;
  1418. public Task<string[]> GetFormatsAsync() => Task.FromResult(Array.Empty<string>());
  1419. public Task<object> GetDataAsync(string format) => Task.FromResult((object)null);
  1420. }
  1421. private class TestTopLevel : TopLevel
  1422. {
  1423. private readonly ILayoutManager _layoutManager;
  1424. public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null)
  1425. : base(impl)
  1426. {
  1427. _layoutManager = layoutManager ?? new LayoutManager(this);
  1428. }
  1429. private protected override ILayoutManager CreateLayoutManager() => _layoutManager;
  1430. }
  1431. private static Mock<ITopLevelImpl> CreateMockTopLevelImpl()
  1432. {
  1433. var clipboard = new Mock<ITopLevelImpl>();
  1434. clipboard.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor());
  1435. clipboard.Setup(r => r.TryGetFeature(typeof(IClipboard)))
  1436. .Returns(new ClipboardStub());
  1437. clipboard.SetupGet(x => x.RenderScaling).Returns(1);
  1438. return clipboard;
  1439. }
  1440. private static FuncControlTemplate<TestTopLevel> CreateTopLevelTemplate()
  1441. {
  1442. return new FuncControlTemplate<TestTopLevel>((x, scope) =>
  1443. new ContentPresenter
  1444. {
  1445. Name = "PART_ContentPresenter",
  1446. [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
  1447. }.RegisterInNameScope(scope));
  1448. }
  1449. private class TestContextMenu : ContextMenu
  1450. {
  1451. public TestContextMenu()
  1452. {
  1453. IsOpen = true;
  1454. }
  1455. }
  1456. }
  1457. }