InputElement_Focus.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. using System;
  2. using Avalonia.Controls;
  3. using Avalonia.Input;
  4. using Avalonia.Interactivity;
  5. using Avalonia.UnitTests;
  6. using Xunit;
  7. namespace Avalonia.Base.UnitTests.Input
  8. {
  9. public class InputElement_Focus
  10. {
  11. [Fact]
  12. public void Focus_Should_Set_FocusManager_Current()
  13. {
  14. Button target;
  15. using (UnitTestApplication.Start(TestServices.RealFocus))
  16. {
  17. var root = new TestRoot
  18. {
  19. Child = target = new Button()
  20. };
  21. target.Focus();
  22. Assert.Same(target, root.FocusManager.GetFocusedElement());
  23. }
  24. }
  25. [Fact]
  26. public void Invisible_Controls_Should_Not_Receive_Focus()
  27. {
  28. Button target;
  29. using (UnitTestApplication.Start(TestServices.RealFocus))
  30. {
  31. var root = new TestRoot
  32. {
  33. Child = target = new Button() { IsVisible = false}
  34. };
  35. Assert.Null(root.FocusManager.GetFocusedElement());
  36. target.Focus();
  37. Assert.False(target.IsFocused);
  38. Assert.False(target.IsKeyboardFocusWithin);
  39. Assert.Null(root.FocusManager.GetFocusedElement());
  40. }
  41. }
  42. [Fact]
  43. public void Effectively_Invisible_Controls_Should_Not_Receive_Focus()
  44. {
  45. var target = new Button();
  46. Panel container;
  47. using (UnitTestApplication.Start(TestServices.RealFocus))
  48. {
  49. var root = new TestRoot
  50. {
  51. Child = container = new Panel
  52. {
  53. IsVisible = false,
  54. Children = { target }
  55. }
  56. };
  57. Assert.Null(root.FocusManager.GetFocusedElement());
  58. target.Focus();
  59. Assert.False(target.IsFocused);
  60. Assert.False(target.IsKeyboardFocusWithin);
  61. Assert.Null(root.FocusManager.GetFocusedElement());
  62. }
  63. }
  64. [Fact]
  65. public void Trying_To_Focus_Invisible_Control_Should_Not_Change_Focus()
  66. {
  67. Button first;
  68. Button second;
  69. using (UnitTestApplication.Start(TestServices.RealFocus))
  70. {
  71. var root = new TestRoot
  72. {
  73. Child = new StackPanel
  74. {
  75. Children =
  76. {
  77. (first = new Button()),
  78. (second = new Button() { IsVisible = false}),
  79. }
  80. }
  81. };
  82. first.Focus();
  83. Assert.Same(first, root.FocusManager.GetFocusedElement());
  84. second.Focus();
  85. Assert.Same(first, root.FocusManager.GetFocusedElement());
  86. }
  87. }
  88. [Fact]
  89. public void Disabled_Controls_Should_Not_Receive_Focus()
  90. {
  91. Button target;
  92. using (UnitTestApplication.Start(TestServices.RealFocus))
  93. {
  94. var root = new TestRoot
  95. {
  96. Child = target = new Button() { IsEnabled = false }
  97. };
  98. Assert.Null(root.FocusManager.GetFocusedElement());
  99. target.Focus();
  100. Assert.False(target.IsFocused);
  101. Assert.False(target.IsKeyboardFocusWithin);
  102. Assert.Null(root.FocusManager.GetFocusedElement());
  103. }
  104. }
  105. [Fact]
  106. public void Effectively_Disabled_Controls_Should_Not_Receive_Focus()
  107. {
  108. var target = new Button();
  109. Panel container;
  110. using (UnitTestApplication.Start(TestServices.RealFocus))
  111. {
  112. var root = new TestRoot
  113. {
  114. Child = container = new Panel
  115. {
  116. IsEnabled = false,
  117. Children = { target }
  118. }
  119. };
  120. Assert.Null(root.FocusManager.GetFocusedElement());
  121. target.Focus();
  122. Assert.False(target.IsFocused);
  123. Assert.False(target.IsKeyboardFocusWithin);
  124. Assert.Null(root.FocusManager.GetFocusedElement());
  125. }
  126. }
  127. [Fact]
  128. public void Focus_Should_Not_Get_Restored_To_Enabled_Control()
  129. {
  130. using (UnitTestApplication.Start(TestServices.RealFocus))
  131. {
  132. var sp = new StackPanel();
  133. Button target = new Button();
  134. Button target1 = new Button();
  135. target.Click += (s, e) => target.IsEnabled = false;
  136. target1.Click += (s, e) => target.IsEnabled = true;
  137. sp.Children.Add(target);
  138. sp.Children.Add(target1);
  139. var root = new TestRoot
  140. {
  141. Child = sp
  142. };
  143. target.Focus();
  144. target.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
  145. Assert.False(target.IsEnabled);
  146. Assert.False(target.IsFocused);
  147. target1.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
  148. Assert.True(target.IsEnabled);
  149. Assert.False(target.IsFocused);
  150. }
  151. }
  152. [Fact]
  153. public void Focus_Should_Be_Cleared_When_Control_Is_Hidden()
  154. {
  155. Button target;
  156. using (UnitTestApplication.Start(TestServices.RealFocus))
  157. {
  158. var root = new TestRoot
  159. {
  160. Child = target = new Button()
  161. };
  162. target.Focus();
  163. target.IsVisible = false;
  164. Assert.Null(root.FocusManager.GetFocusedElement());
  165. }
  166. }
  167. [Fact(Skip = "Need to implement IsEffectivelyVisible change notifications.")]
  168. public void Focus_Should_Be_Cleared_When_Control_Is_Effectively_Hidden()
  169. {
  170. Border container;
  171. Button target;
  172. using (UnitTestApplication.Start(TestServices.RealFocus))
  173. {
  174. var root = new TestRoot
  175. {
  176. Child = container = new Border
  177. {
  178. Child = target = new Button(),
  179. }
  180. };
  181. target.Focus();
  182. container.IsVisible = false;
  183. Assert.Null(root.FocusManager.GetFocusedElement());
  184. }
  185. }
  186. [Fact]
  187. public void Focus_Should_Be_Cleared_When_Control_Is_Disabled()
  188. {
  189. Button target;
  190. using (UnitTestApplication.Start(TestServices.RealFocus))
  191. {
  192. var root = new TestRoot
  193. {
  194. Child = target = new Button()
  195. };
  196. target.Focus();
  197. target.IsEnabled = false;
  198. Assert.Null(root.FocusManager.GetFocusedElement());
  199. }
  200. }
  201. [Fact]
  202. public void Focus_Should_Be_Cleared_When_Control_Is_Effectively_Disabled()
  203. {
  204. Border container;
  205. Button target;
  206. using (UnitTestApplication.Start(TestServices.RealFocus))
  207. {
  208. var root = new TestRoot
  209. {
  210. Child = container = new Border
  211. {
  212. Child = target = new Button(),
  213. }
  214. };
  215. target.Focus();
  216. container.IsEnabled = false;
  217. Assert.Null(root.FocusManager.GetFocusedElement());
  218. }
  219. }
  220. [Fact]
  221. public void Focus_Should_Be_Cleared_When_Control_Is_Removed_From_VisualTree()
  222. {
  223. Button target;
  224. using (UnitTestApplication.Start(TestServices.RealFocus))
  225. {
  226. var root = new TestRoot
  227. {
  228. Child = target = new Button()
  229. };
  230. target.Focus();
  231. root.Child = null;
  232. Assert.Null(root.FocusManager.GetFocusedElement());
  233. }
  234. }
  235. [Fact]
  236. public void Focus_Pseudoclass_Should_Be_Applied_On_Focus()
  237. {
  238. using (UnitTestApplication.Start(TestServices.RealFocus))
  239. {
  240. var target1 = new Decorator { Focusable = true };
  241. var target2 = new Decorator { Focusable = true };
  242. var root = new TestRoot
  243. {
  244. Child = new StackPanel
  245. {
  246. Children =
  247. {
  248. target1,
  249. target2
  250. }
  251. }
  252. };
  253. target1.ApplyTemplate();
  254. target2.ApplyTemplate();
  255. target1.Focus();
  256. Assert.True(target1.IsFocused);
  257. Assert.True(target1.Classes.Contains(":focus"));
  258. Assert.False(target2.IsFocused);
  259. Assert.False(target2.Classes.Contains(":focus"));
  260. target2.Focus(NavigationMethod.Tab);
  261. Assert.False(target1.IsFocused);
  262. Assert.False(target1.Classes.Contains(":focus"));
  263. Assert.True(target2.IsFocused);
  264. Assert.True(target2.Classes.Contains(":focus"));
  265. }
  266. }
  267. [Fact]
  268. public void Control_FocusVsisible_Pseudoclass_Should_Be_Applied_On_Tab_And_DirectionalFocus()
  269. {
  270. using (UnitTestApplication.Start(TestServices.RealFocus))
  271. {
  272. var target1 = new Decorator { Focusable = true };
  273. var target2 = new Decorator { Focusable = true };
  274. var root = new TestRoot
  275. {
  276. Child = new StackPanel
  277. {
  278. Children =
  279. {
  280. target1,
  281. target2
  282. }
  283. }
  284. };
  285. target1.ApplyTemplate();
  286. target2.ApplyTemplate();
  287. target1.Focus();
  288. Assert.True(target1.IsFocused);
  289. Assert.False(target1.Classes.Contains(":focus-visible"));
  290. Assert.False(target2.IsFocused);
  291. Assert.False(target2.Classes.Contains(":focus-visible"));
  292. target2.Focus(NavigationMethod.Tab);
  293. Assert.False(target1.IsFocused);
  294. Assert.False(target1.Classes.Contains(":focus-visible"));
  295. Assert.True(target2.IsFocused);
  296. Assert.True(target2.Classes.Contains(":focus-visible"));
  297. target1.Focus(NavigationMethod.Directional);
  298. Assert.True(target1.IsFocused);
  299. Assert.True(target1.Classes.Contains(":focus-visible"));
  300. Assert.False(target2.IsFocused);
  301. Assert.False(target2.Classes.Contains(":focus-visible"));
  302. }
  303. }
  304. [Fact]
  305. public void Control_FocusWithin_PseudoClass_Should_Be_Applied()
  306. {
  307. using (UnitTestApplication.Start(TestServices.RealFocus))
  308. {
  309. var target1 = new Decorator { Focusable = true };
  310. var target2 = new Decorator { Focusable = true };
  311. var root = new TestRoot
  312. {
  313. Child = new StackPanel
  314. {
  315. Children =
  316. {
  317. target1,
  318. target2
  319. }
  320. }
  321. };
  322. target1.ApplyTemplate();
  323. target2.ApplyTemplate();
  324. target1.Focus();
  325. Assert.True(target1.IsFocused);
  326. Assert.True(target1.Classes.Contains(":focus-within"));
  327. Assert.True(target1.IsKeyboardFocusWithin);
  328. Assert.True(root.Child.Classes.Contains(":focus-within"));
  329. Assert.True(root.Child.IsKeyboardFocusWithin);
  330. Assert.True(root.Classes.Contains(":focus-within"));
  331. Assert.True(root.IsKeyboardFocusWithin);
  332. }
  333. }
  334. [Fact]
  335. public void Control_FocusWithin_PseudoClass_Should_Be_Applied_and_Removed()
  336. {
  337. using (UnitTestApplication.Start(TestServices.RealFocus))
  338. {
  339. var target1 = new Decorator { Focusable = true };
  340. var target2 = new Decorator { Focusable = true };
  341. var panel1 = new Panel { Children = { target1 } };
  342. var panel2 = new Panel { Children = { target2 } };
  343. var root = new TestRoot
  344. {
  345. Child = new StackPanel
  346. {
  347. Children =
  348. {
  349. panel1,
  350. panel2
  351. }
  352. }
  353. };
  354. target1.ApplyTemplate();
  355. target2.ApplyTemplate();
  356. target1.Focus();
  357. Assert.True(target1.IsFocused);
  358. Assert.True(target1.Classes.Contains(":focus-within"));
  359. Assert.True(target1.IsKeyboardFocusWithin);
  360. Assert.True(panel1.Classes.Contains(":focus-within"));
  361. Assert.True(panel1.IsKeyboardFocusWithin);
  362. Assert.True(root.Child.Classes.Contains(":focus-within"));
  363. Assert.True(root.Child.IsKeyboardFocusWithin);
  364. Assert.True(root.Classes.Contains(":focus-within"));
  365. Assert.True(root.IsKeyboardFocusWithin);
  366. target2.Focus();
  367. Assert.False(target1.IsFocused);
  368. Assert.False(target1.Classes.Contains(":focus-within"));
  369. Assert.False(target1.IsKeyboardFocusWithin);
  370. Assert.False(panel1.Classes.Contains(":focus-within"));
  371. Assert.False(panel1.IsKeyboardFocusWithin);
  372. Assert.True(root.Child.Classes.Contains(":focus-within"));
  373. Assert.True(root.Child.IsKeyboardFocusWithin);
  374. Assert.True(root.Classes.Contains(":focus-within"));
  375. Assert.True(root.IsKeyboardFocusWithin);
  376. Assert.True(target2.IsFocused);
  377. Assert.True(target2.Classes.Contains(":focus-within"));
  378. Assert.True(target2.IsKeyboardFocusWithin);
  379. Assert.True(panel2.Classes.Contains(":focus-within"));
  380. Assert.True(panel2.IsKeyboardFocusWithin);
  381. }
  382. }
  383. [Fact]
  384. public void Control_FocusWithin_Pseudoclass_Should_Be_Removed_When_Removed_From_Tree()
  385. {
  386. using (UnitTestApplication.Start(TestServices.RealFocus))
  387. {
  388. var target1 = new Decorator { Focusable = true };
  389. var target2 = new Decorator { Focusable = true };
  390. var root = new TestRoot
  391. {
  392. Child = new StackPanel
  393. {
  394. Children =
  395. {
  396. target1,
  397. target2
  398. }
  399. }
  400. };
  401. target1.ApplyTemplate();
  402. target2.ApplyTemplate();
  403. target1.Focus();
  404. Assert.True(target1.IsFocused);
  405. Assert.True(target1.Classes.Contains(":focus-within"));
  406. Assert.True(target1.IsKeyboardFocusWithin);
  407. Assert.True(root.Child.Classes.Contains(":focus-within"));
  408. Assert.True(root.Child.IsKeyboardFocusWithin);
  409. Assert.True(root.Classes.Contains(":focus-within"));
  410. Assert.True(root.IsKeyboardFocusWithin);
  411. Assert.Equal(KeyboardDevice.Instance.FocusedElement, target1);
  412. root.Child = null;
  413. Assert.Null(KeyboardDevice.Instance.FocusedElement);
  414. Assert.False(target1.IsFocused);
  415. Assert.False(target1.Classes.Contains(":focus-within"));
  416. Assert.False(target1.IsKeyboardFocusWithin);
  417. Assert.False(root.Classes.Contains(":focus-within"));
  418. Assert.False(root.IsKeyboardFocusWithin);
  419. }
  420. }
  421. [Fact]
  422. public void Control_FocusWithin_Pseudoclass_Should_Be_Removed_Focus_Moves_To_Different_Root()
  423. {
  424. using (UnitTestApplication.Start(TestServices.RealFocus))
  425. {
  426. var target1 = new Decorator { Focusable = true };
  427. var target2 = new Decorator { Focusable = true };
  428. var root1 = new TestRoot
  429. {
  430. Child = new StackPanel
  431. {
  432. Children =
  433. {
  434. target1,
  435. }
  436. }
  437. };
  438. var root2 = new TestRoot
  439. {
  440. Child = new StackPanel
  441. {
  442. Children =
  443. {
  444. target2,
  445. }
  446. }
  447. };
  448. target1.ApplyTemplate();
  449. target2.ApplyTemplate();
  450. target1.Focus();
  451. Assert.True(target1.IsFocused);
  452. Assert.True(target1.Classes.Contains(":focus-within"));
  453. Assert.True(target1.IsKeyboardFocusWithin);
  454. Assert.True(root1.Child.Classes.Contains(":focus-within"));
  455. Assert.True(root1.Child.IsKeyboardFocusWithin);
  456. Assert.True(root1.Classes.Contains(":focus-within"));
  457. Assert.True(root1.IsKeyboardFocusWithin);
  458. Assert.Equal(KeyboardDevice.Instance.FocusedElement, target1);
  459. target2.Focus();
  460. Assert.False(target1.IsFocused);
  461. Assert.False(target1.Classes.Contains(":focus-within"));
  462. Assert.False(target1.IsKeyboardFocusWithin);
  463. Assert.False(root1.Child.Classes.Contains(":focus-within"));
  464. Assert.False(root1.Child.IsKeyboardFocusWithin);
  465. Assert.False(root1.Classes.Contains(":focus-within"));
  466. Assert.False(root1.IsKeyboardFocusWithin);
  467. Assert.True(target2.IsFocused);
  468. Assert.True(target2.Classes.Contains(":focus-within"));
  469. Assert.True(target2.IsKeyboardFocusWithin);
  470. Assert.True(root2.Child.Classes.Contains(":focus-within"));
  471. Assert.True(root2.Child.IsKeyboardFocusWithin);
  472. Assert.True(root2.Classes.Contains(":focus-within"));
  473. Assert.True(root2.IsKeyboardFocusWithin);
  474. }
  475. }
  476. [Fact]
  477. public void Can_Clear_Focus()
  478. {
  479. Button target;
  480. using (UnitTestApplication.Start(TestServices.RealFocus))
  481. {
  482. var root = new TestRoot
  483. {
  484. Child = target = new Button()
  485. };
  486. target.Focus();
  487. root.FocusManager.ClearFocus();
  488. Assert.Null(root.FocusManager.GetFocusedElement());
  489. }
  490. }
  491. [Fact]
  492. public void Removing_Focused_Element_Inside_Focus_Scope_Activates_Root_Focus_Scope()
  493. {
  494. // Issue #13325
  495. using var app = UnitTestApplication.Start(TestServices.RealFocus);
  496. Button innerButton, intermediateButton, outerButton;
  497. TestFocusScope innerScope;
  498. var root = new TestRoot
  499. {
  500. Child = new StackPanel
  501. {
  502. Children =
  503. {
  504. // Intermediate focus scope to make sure that the root focus scope gets
  505. // activated, not this one.
  506. new TestFocusScope
  507. {
  508. Children =
  509. {
  510. (innerScope = new TestFocusScope
  511. {
  512. Children =
  513. {
  514. (innerButton = new Button()),
  515. }
  516. }),
  517. (intermediateButton = new Button()),
  518. }
  519. },
  520. (outerButton = new Button()),
  521. }
  522. }
  523. };
  524. // Focus a control in each scope, ending with the innermost one.
  525. outerButton.Focus();
  526. intermediateButton.Focus();
  527. innerButton.Focus();
  528. // Remove the focused control from the tree.
  529. ((Panel)innerButton.Parent).Children.Remove(innerButton);
  530. var focusManager = Assert.IsType<FocusManager>(root.FocusManager);
  531. Assert.Same(outerButton, focusManager.GetFocusedElement());
  532. Assert.Null(focusManager.GetFocusedElement(innerScope));
  533. }
  534. [Fact]
  535. public void Removing_Focus_Scope_Activates_Root_Focus_Scope()
  536. {
  537. using var app = UnitTestApplication.Start(TestServices.RealFocus);
  538. Button innerButton, outerButton;
  539. TestFocusScope innerScope;
  540. var root = new TestRoot
  541. {
  542. Child = new StackPanel
  543. {
  544. Children =
  545. {
  546. (innerScope = new TestFocusScope
  547. {
  548. Children =
  549. {
  550. (innerButton = new Button()),
  551. }
  552. }),
  553. (outerButton = new Button()),
  554. }
  555. }
  556. };
  557. // Focus a control in the top-level and inner focus scopes.
  558. outerButton.Focus();
  559. innerButton.Focus();
  560. // Remove the inner focus scope.
  561. ((Panel)innerScope.Parent).Children.Remove(innerScope);
  562. var focusManager = Assert.IsType<FocusManager>(root.FocusManager);
  563. Assert.Same(outerButton, focusManager.GetFocusedElement());
  564. }
  565. [Fact]
  566. public void Switching_Focus_Scope_Changes_Focus()
  567. {
  568. using var app = UnitTestApplication.Start(TestServices.RealFocus);
  569. Button innerButton, outerButton;
  570. TestFocusScope innerScope;
  571. var root = new TestRoot
  572. {
  573. Child = new StackPanel
  574. {
  575. Children =
  576. {
  577. (innerScope = new TestFocusScope
  578. {
  579. Children =
  580. {
  581. (innerButton = new Button()),
  582. }
  583. }),
  584. (outerButton = new Button()),
  585. }
  586. }
  587. };
  588. // Focus a control in the top-level and inner focus scopes.
  589. outerButton.Focus();
  590. innerButton.Focus();
  591. var focusManager = Assert.IsType<FocusManager>(root.FocusManager);
  592. Assert.Same(innerButton, focusManager.GetFocusedElement());
  593. focusManager.SetFocusScope(root);
  594. Assert.Same(outerButton, focusManager.GetFocusedElement());
  595. focusManager.SetFocusScope(innerScope);
  596. Assert.Same(innerButton, focusManager.GetFocusedElement());
  597. }
  598. private class TestFocusScope : Panel, IFocusScope
  599. {
  600. }
  601. }
  602. }