CommandBarTests.cs 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.Linq;
  5. using Avalonia.Controls;
  6. using Avalonia.Controls.Primitives;
  7. using Avalonia.Input;
  8. using Avalonia.LogicalTree;
  9. using Avalonia.Threading;
  10. using Avalonia.UnitTests;
  11. using Avalonia.VisualTree;
  12. using Xunit;
  13. namespace Avalonia.Controls.UnitTests;
  14. public class CommandBarButtonTests : ScopedTestBase
  15. {
  16. [Fact]
  17. public void Label_DefaultIsNull()
  18. => Assert.Null(new CommandBarButton().Label);
  19. [Fact]
  20. public void Label_RoundTrip()
  21. {
  22. var btn = new CommandBarButton { Label = "Save" };
  23. Assert.Equal("Save", btn.Label);
  24. }
  25. [Fact]
  26. public void Icon_DefaultIsNull()
  27. => Assert.Null(new CommandBarButton().Icon);
  28. [Fact]
  29. public void Icon_RoundTrip()
  30. {
  31. var btn = new CommandBarButton();
  32. var icon = new object();
  33. btn.Icon = icon;
  34. Assert.Same(icon, btn.Icon);
  35. }
  36. [Fact]
  37. public void IsCompact_DefaultIsFalse()
  38. => Assert.False(new CommandBarButton().IsCompact);
  39. [Fact]
  40. public void IsCompact_RoundTrip()
  41. {
  42. var btn = new CommandBarButton { IsCompact = true };
  43. Assert.True(btn.IsCompact);
  44. }
  45. [Fact]
  46. public void DynamicOverflowOrder_DefaultIsZero()
  47. => Assert.Equal(0, new CommandBarButton().DynamicOverflowOrder);
  48. [Fact]
  49. public void DynamicOverflowOrder_RoundTrip()
  50. {
  51. var btn = new CommandBarButton { DynamicOverflowOrder = 3 };
  52. Assert.Equal(3, btn.DynamicOverflowOrder);
  53. }
  54. [Fact]
  55. public void LabelPosition_DefaultIsBottom()
  56. => Assert.Equal(CommandBarDefaultLabelPosition.Bottom, new CommandBarButton().LabelPosition);
  57. [Fact]
  58. public void LabelPosition_RoundTrip()
  59. {
  60. var btn = new CommandBarButton { LabelPosition = CommandBarDefaultLabelPosition.Right };
  61. Assert.Equal(CommandBarDefaultLabelPosition.Right, btn.LabelPosition);
  62. }
  63. [Fact]
  64. public void IsInOverflow_DefaultIsFalse()
  65. => Assert.False(new CommandBarButton().IsInOverflow);
  66. [Fact]
  67. public void IsInOverflow_RoundTrip()
  68. {
  69. var btn = new CommandBarButton { IsInOverflow = true };
  70. Assert.True(btn.IsInOverflow);
  71. }
  72. [Fact]
  73. public void ImplementsICommandBarElement()
  74. => Assert.IsAssignableFrom<ICommandBarElement>(new CommandBarButton());
  75. [Fact]
  76. public void ICommandBarElement_IsCompact_ReadWrite()
  77. {
  78. ICommandBarElement elem = new CommandBarButton();
  79. elem.IsCompact = true;
  80. Assert.True(elem.IsCompact);
  81. }
  82. [Fact]
  83. public void Command_DefaultIsNull()
  84. => Assert.Null(new CommandBarButton().Command);
  85. [Fact]
  86. public void CommandParameter_DefaultIsNull()
  87. => Assert.Null(new CommandBarButton().CommandParameter);
  88. [Fact]
  89. public void Command_RoundTrip()
  90. {
  91. var btn = new CommandBarButton();
  92. var cmd = new DelegateCommand(_ => { });
  93. btn.Command = cmd;
  94. Assert.Same(cmd, btn.Command);
  95. }
  96. [Fact]
  97. public void CommandParameter_RoundTrip()
  98. {
  99. var btn = new CommandBarButton { CommandParameter = "param" };
  100. Assert.Equal("param", btn.CommandParameter);
  101. }
  102. }
  103. public class CommandBarToggleButtonTests : ScopedTestBase
  104. {
  105. [Fact]
  106. public void Label_DefaultIsNull()
  107. => Assert.Null(new CommandBarToggleButton().Label);
  108. [Fact]
  109. public void Label_RoundTrip()
  110. {
  111. var btn = new CommandBarToggleButton { Label = "Bold" };
  112. Assert.Equal("Bold", btn.Label);
  113. }
  114. [Fact]
  115. public void Icon_DefaultIsNull()
  116. => Assert.Null(new CommandBarToggleButton().Icon);
  117. [Fact]
  118. public void IsCompact_DefaultIsFalse()
  119. => Assert.False(new CommandBarToggleButton().IsCompact);
  120. [Fact]
  121. public void IsCompact_RoundTrip()
  122. {
  123. var btn = new CommandBarToggleButton { IsCompact = true };
  124. Assert.True(btn.IsCompact);
  125. }
  126. [Fact]
  127. public void DynamicOverflowOrder_DefaultIsZero()
  128. => Assert.Equal(0, new CommandBarToggleButton().DynamicOverflowOrder);
  129. [Fact]
  130. public void DynamicOverflowOrder_RoundTrip()
  131. {
  132. var btn = new CommandBarToggleButton { DynamicOverflowOrder = 5 };
  133. Assert.Equal(5, btn.DynamicOverflowOrder);
  134. }
  135. [Fact]
  136. public void LabelPosition_DefaultIsBottom()
  137. => Assert.Equal(CommandBarDefaultLabelPosition.Bottom, new CommandBarToggleButton().LabelPosition);
  138. [Fact]
  139. public void LabelPosition_RoundTrip()
  140. {
  141. var btn = new CommandBarToggleButton { LabelPosition = CommandBarDefaultLabelPosition.Collapsed };
  142. Assert.Equal(CommandBarDefaultLabelPosition.Collapsed, btn.LabelPosition);
  143. }
  144. [Fact]
  145. public void IsInOverflow_DefaultIsFalse()
  146. => Assert.False(new CommandBarToggleButton().IsInOverflow);
  147. [Fact]
  148. public void ImplementsICommandBarElement()
  149. => Assert.IsAssignableFrom<ICommandBarElement>(new CommandBarToggleButton());
  150. [Fact]
  151. public void ICommandBarElement_IsCompact_ReadWrite()
  152. {
  153. ICommandBarElement elem = new CommandBarToggleButton();
  154. elem.IsCompact = true;
  155. Assert.True(elem.IsCompact);
  156. }
  157. [Fact]
  158. public void Command_DefaultIsNull()
  159. => Assert.Null(new CommandBarToggleButton().Command);
  160. [Fact]
  161. public void Command_RoundTrip()
  162. {
  163. var btn = new CommandBarToggleButton();
  164. var cmd = new DelegateCommand(_ => { });
  165. btn.Command = cmd;
  166. Assert.Same(cmd, btn.Command);
  167. }
  168. [Fact]
  169. public void CommandParameter_RoundTrip()
  170. {
  171. var btn = new CommandBarToggleButton { CommandParameter = 42 };
  172. Assert.Equal(42, btn.CommandParameter);
  173. }
  174. }
  175. public class CommandBarSeparatorTests : ScopedTestBase
  176. {
  177. [Fact]
  178. public void IsCompact_DefaultIsFalse()
  179. => Assert.False(new CommandBarSeparator().IsCompact);
  180. [Fact]
  181. public void IsCompact_RoundTrip()
  182. {
  183. var sep = new CommandBarSeparator { IsCompact = true };
  184. Assert.True(sep.IsCompact);
  185. }
  186. [Fact]
  187. public void IsInOverflow_DefaultIsFalse()
  188. => Assert.False(new CommandBarSeparator().IsInOverflow);
  189. [Fact]
  190. public void IsInOverflow_RoundTrip()
  191. {
  192. var sep = new CommandBarSeparator { IsInOverflow = true };
  193. Assert.True(sep.IsInOverflow);
  194. }
  195. [Fact]
  196. public void ImplementsICommandBarElement()
  197. => Assert.IsAssignableFrom<ICommandBarElement>(new CommandBarSeparator());
  198. [Fact]
  199. public void DerivesFromSeparator()
  200. => Assert.IsAssignableFrom<Separator>(new CommandBarSeparator());
  201. [Fact]
  202. public void ICommandBarElement_IsCompact_ReadWrite()
  203. {
  204. ICommandBarElement elem = new CommandBarSeparator();
  205. elem.IsCompact = true;
  206. Assert.True(elem.IsCompact);
  207. }
  208. }
  209. public class CommandBarEnumTests : ScopedTestBase
  210. {
  211. [Fact]
  212. public void LabelPosition_Bottom_IsZero()
  213. => Assert.Equal(0, (int)CommandBarDefaultLabelPosition.Bottom);
  214. [Fact]
  215. public void LabelPosition_Right_IsOne()
  216. => Assert.Equal(1, (int)CommandBarDefaultLabelPosition.Right);
  217. [Fact]
  218. public void LabelPosition_Collapsed_IsTwo()
  219. => Assert.Equal(2, (int)CommandBarDefaultLabelPosition.Collapsed);
  220. [Fact]
  221. public void OverflowButtonVisibility_Auto_IsZero()
  222. => Assert.Equal(0, (int)CommandBarOverflowButtonVisibility.Auto);
  223. [Fact]
  224. public void OverflowButtonVisibility_Visible_IsOne()
  225. => Assert.Equal(1, (int)CommandBarOverflowButtonVisibility.Visible);
  226. [Fact]
  227. public void OverflowButtonVisibility_Collapsed_IsTwo()
  228. => Assert.Equal(2, (int)CommandBarOverflowButtonVisibility.Collapsed);
  229. }
  230. public class CommandBarDefaultsTests : ScopedTestBase
  231. {
  232. [Fact]
  233. public void DefaultLabelPosition_IsBottom()
  234. => Assert.Equal(CommandBarDefaultLabelPosition.Bottom, new CommandBar().DefaultLabelPosition);
  235. [Fact]
  236. public void OverflowButtonVisibility_DefaultIsAuto()
  237. => Assert.Equal(CommandBarOverflowButtonVisibility.Auto, new CommandBar().OverflowButtonVisibility);
  238. [Fact]
  239. public void IsOpen_DefaultIsFalse()
  240. => Assert.False(new CommandBar().IsOpen);
  241. [Fact]
  242. public void IsSticky_DefaultIsFalse()
  243. => Assert.False(new CommandBar().IsSticky);
  244. [Fact]
  245. public void IsDynamicOverflowEnabled_DefaultIsFalse()
  246. => Assert.False(new CommandBar().IsDynamicOverflowEnabled);
  247. [Fact]
  248. public void HasSecondaryCommands_DefaultIsFalse()
  249. => Assert.False(new CommandBar().HasSecondaryCommands);
  250. [Fact]
  251. public void IsOverflowButtonVisible_DefaultIsFalse()
  252. => Assert.False(new CommandBar().IsOverflowButtonVisible);
  253. [Fact]
  254. public void Content_DefaultIsNull()
  255. => Assert.Null(new CommandBar().Content);
  256. [Fact]
  257. public void PrimaryCommands_NotNull()
  258. => Assert.NotNull(new CommandBar().PrimaryCommands);
  259. [Fact]
  260. public void SecondaryCommands_NotNull()
  261. => Assert.NotNull(new CommandBar().SecondaryCommands);
  262. [Fact]
  263. public void VisiblePrimaryCommands_NotNull()
  264. => Assert.NotNull(new CommandBar().VisiblePrimaryCommands);
  265. [Fact]
  266. public void OverflowItems_NotNull()
  267. => Assert.NotNull(new CommandBar().OverflowItems);
  268. [Fact]
  269. public void PrimaryCommands_StartsEmpty()
  270. => Assert.Empty(new CommandBar().PrimaryCommands);
  271. [Fact]
  272. public void SecondaryCommands_StartsEmpty()
  273. => Assert.Empty(new CommandBar().SecondaryCommands);
  274. [Fact]
  275. public void PrimaryCommands_ReturnsNewListWhenNull()
  276. {
  277. var cb = new CommandBar();
  278. cb.ClearValue(CommandBar.PrimaryCommandsProperty);
  279. Assert.NotNull(cb.PrimaryCommands);
  280. Assert.Empty(cb.PrimaryCommands);
  281. }
  282. [Fact]
  283. public void SecondaryCommands_ReturnsNewListWhenNull()
  284. {
  285. var cb = new CommandBar();
  286. cb.ClearValue(CommandBar.SecondaryCommandsProperty);
  287. Assert.NotNull(cb.SecondaryCommands);
  288. Assert.Empty(cb.SecondaryCommands);
  289. }
  290. [Fact]
  291. public void VisiblePrimaryCommands_StartsEmpty()
  292. => Assert.Empty(new CommandBar().VisiblePrimaryCommands);
  293. [Fact]
  294. public void OverflowItems_StartsEmpty()
  295. => Assert.Empty(new CommandBar().OverflowItems);
  296. [Fact]
  297. public void ItemWidthBottom_DefaultIs70()
  298. => Assert.Equal(70d, new CommandBar().ItemWidthBottom);
  299. [Fact]
  300. public void ItemWidthRight_DefaultIs102()
  301. => Assert.Equal(102d, new CommandBar().ItemWidthRight);
  302. [Fact]
  303. public void ItemWidthCollapsed_DefaultIs42()
  304. => Assert.Equal(42d, new CommandBar().ItemWidthCollapsed);
  305. }
  306. public class CommandBarPropertyRoundTripTests : ScopedTestBase
  307. {
  308. [Fact]
  309. public void Content_RoundTrip()
  310. {
  311. var cb = new CommandBar();
  312. var content = new object();
  313. cb.Content = content;
  314. Assert.Same(content, cb.Content);
  315. }
  316. [Fact]
  317. public void DefaultLabelPosition_RoundTrip()
  318. {
  319. var cb = new CommandBar { DefaultLabelPosition = CommandBarDefaultLabelPosition.Right };
  320. Assert.Equal(CommandBarDefaultLabelPosition.Right, cb.DefaultLabelPosition);
  321. }
  322. [Fact]
  323. public void IsOpen_RoundTrip()
  324. {
  325. var cb = new CommandBar { IsOpen = true };
  326. Assert.True(cb.IsOpen);
  327. cb.IsOpen = false;
  328. Assert.False(cb.IsOpen);
  329. }
  330. [Fact]
  331. public void IsSticky_RoundTrip()
  332. {
  333. var cb = new CommandBar { IsSticky = true };
  334. Assert.True(cb.IsSticky);
  335. }
  336. [Fact]
  337. public void IsDynamicOverflowEnabled_RoundTrip()
  338. {
  339. var cb = new CommandBar { IsDynamicOverflowEnabled = true };
  340. Assert.True(cb.IsDynamicOverflowEnabled);
  341. }
  342. [Fact]
  343. public void OverflowButtonVisibility_RoundTrip()
  344. {
  345. var cb = new CommandBar { OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible };
  346. Assert.Equal(CommandBarOverflowButtonVisibility.Visible, cb.OverflowButtonVisibility);
  347. }
  348. [Fact]
  349. public void ItemWidthBottom_RoundTrip()
  350. {
  351. var cb = new CommandBar { ItemWidthBottom = 80d };
  352. Assert.Equal(80d, cb.ItemWidthBottom);
  353. }
  354. [Fact]
  355. public void ItemWidthRight_RoundTrip()
  356. {
  357. var cb = new CommandBar { ItemWidthRight = 120d };
  358. Assert.Equal(120d, cb.ItemWidthRight);
  359. }
  360. [Fact]
  361. public void ItemWidthCollapsed_RoundTrip()
  362. {
  363. var cb = new CommandBar { ItemWidthCollapsed = 50d };
  364. Assert.Equal(50d, cb.ItemWidthCollapsed);
  365. }
  366. }
  367. public class CommandBarIsOpenTests : ScopedTestBase
  368. {
  369. [Fact]
  370. public void Opening_FiredWhenIsOpenBecomesTrue()
  371. {
  372. var cb = new CommandBar();
  373. bool fired = false;
  374. cb.Opening += (_, _) => fired = true;
  375. cb.IsOpen = true;
  376. Assert.True(fired);
  377. }
  378. [Fact]
  379. public void Opened_FiredWhenIsOpenBecomesTrue()
  380. {
  381. var cb = new CommandBar();
  382. bool fired = false;
  383. cb.Opened += (_, _) => fired = true;
  384. cb.IsOpen = true;
  385. Assert.True(fired);
  386. }
  387. [Fact]
  388. public void Closing_FiredWhenIsOpenBecomesFalse()
  389. {
  390. var cb = new CommandBar { IsOpen = true };
  391. bool fired = false;
  392. cb.Closing += (_, _) => fired = true;
  393. cb.IsOpen = false;
  394. Assert.True(fired);
  395. }
  396. [Fact]
  397. public void Closed_FiredWhenIsOpenBecomesFalse()
  398. {
  399. var cb = new CommandBar { IsOpen = true };
  400. bool fired = false;
  401. cb.Closed += (_, _) => fired = true;
  402. cb.IsOpen = false;
  403. Assert.True(fired);
  404. }
  405. [Fact]
  406. public void Opening_NotFiredWhenAlreadyOpen()
  407. {
  408. var cb = new CommandBar();
  409. cb.IsOpen = true;
  410. int count = 0;
  411. cb.Opening += (_, _) => count++;
  412. cb.IsOpen = true;
  413. Assert.Equal(0, count);
  414. }
  415. [Fact]
  416. public void Closing_NotFiredWhenAlreadyClosed()
  417. {
  418. var cb = new CommandBar();
  419. int count = 0;
  420. cb.Closing += (_, _) => count++;
  421. cb.IsOpen = false;
  422. Assert.Equal(0, count);
  423. }
  424. [Fact]
  425. public void Events_FiredInOrder_OpenThenClose()
  426. {
  427. var cb = new CommandBar();
  428. var events = new List<string>();
  429. cb.Opening += (_, _) => events.Add("Opening");
  430. cb.Opened += (_, _) => events.Add("Opened");
  431. cb.Closing += (_, _) => events.Add("Closing");
  432. cb.Closed += (_, _) => events.Add("Closed");
  433. cb.IsOpen = true;
  434. cb.IsOpen = false;
  435. Assert.Equal(new[] { "Opening", "Opened", "Closing", "Closed" }, events);
  436. }
  437. }
  438. public class CommandBarCollectionTests : ScopedTestBase
  439. {
  440. [Fact]
  441. public void PrimaryCommands_Added_AppearInVisiblePrimary_WhenDynamicOverflowDisabled()
  442. {
  443. var cb = new CommandBar();
  444. var btn = new CommandBarButton { Label = "Save" };
  445. cb.PrimaryCommands!.Add(btn);
  446. Assert.Contains(btn, cb.VisiblePrimaryCommands);
  447. }
  448. [Fact]
  449. public void PrimaryCommands_DefaultCollection_DoesNotDuplicateVisiblePrimaryNotifications()
  450. {
  451. var cb = new CommandBar();
  452. var notifications = 0;
  453. ((INotifyCollectionChanged)cb.VisiblePrimaryCommands).CollectionChanged += (_, _) => notifications++;
  454. cb.PrimaryCommands!.Add(new CommandBarButton { Label = "Save" });
  455. Assert.Equal(2, notifications);
  456. }
  457. [Fact]
  458. public void PrimaryCommands_Removed_DisappearsFromVisiblePrimary()
  459. {
  460. var cb = new CommandBar();
  461. var btn = new CommandBarButton { Label = "Save" };
  462. cb.PrimaryCommands!.Add(btn);
  463. cb.PrimaryCommands!.Remove(btn);
  464. Assert.DoesNotContain(btn, cb.VisiblePrimaryCommands);
  465. }
  466. [Fact]
  467. public void SecondaryCommands_Added_AppearInOverflowItems()
  468. {
  469. var cb = new CommandBar();
  470. var btn = new CommandBarButton { Label = "Settings" };
  471. cb.SecondaryCommands!.Add(btn);
  472. Assert.Contains(btn, cb.OverflowItems);
  473. }
  474. [Fact]
  475. public void SecondaryCommands_DefaultCollection_DoesNotDuplicateOverflowNotifications()
  476. {
  477. var cb = new CommandBar();
  478. var notifications = 0;
  479. ((INotifyCollectionChanged)cb.OverflowItems).CollectionChanged += (_, _) => notifications++;
  480. cb.SecondaryCommands!.Add(new CommandBarButton { Label = "Settings" });
  481. Assert.Equal(2, notifications);
  482. }
  483. [Fact]
  484. public void SecondaryCommands_Removed_DisappearsFromOverflowItems()
  485. {
  486. var cb = new CommandBar();
  487. var btn = new CommandBarButton { Label = "Settings" };
  488. cb.SecondaryCommands!.Add(btn);
  489. cb.SecondaryCommands!.Remove(btn);
  490. Assert.DoesNotContain(btn, cb.OverflowItems);
  491. }
  492. [Fact]
  493. public void HasSecondaryCommands_TrueWhenSecondaryAdded()
  494. {
  495. var cb = new CommandBar();
  496. cb.SecondaryCommands!.Add(new CommandBarButton { Label = "Options" });
  497. Assert.True(cb.HasSecondaryCommands);
  498. }
  499. [Fact]
  500. public void HasSecondaryCommands_FalseAfterSecondaryCleared()
  501. {
  502. var cb = new CommandBar();
  503. var btn = new CommandBarButton { Label = "Options" };
  504. cb.SecondaryCommands!.Add(btn);
  505. cb.SecondaryCommands!.Remove(btn);
  506. Assert.False(cb.HasSecondaryCommands);
  507. }
  508. [Fact]
  509. public void OverflowItems_CountMatchesSecondaryCommandCount()
  510. {
  511. var cb = new CommandBar();
  512. cb.SecondaryCommands!.Add(new CommandBarButton());
  513. cb.SecondaryCommands!.Add(new CommandBarButton());
  514. Assert.Equal(2, cb.OverflowItems.Count);
  515. }
  516. [Fact]
  517. public void VisiblePrimaryCommands_CountMatchesPrimary_WhenDynamicOverflowDisabled()
  518. {
  519. var cb = new CommandBar();
  520. cb.PrimaryCommands!.Add(new CommandBarButton());
  521. cb.PrimaryCommands!.Add(new CommandBarButton());
  522. Assert.Equal(2, cb.VisiblePrimaryCommands.Count);
  523. }
  524. [Fact]
  525. public void MultiplePrimaryCommands_AllVisibleInOrder()
  526. {
  527. var cb = new CommandBar();
  528. var btn1 = new CommandBarButton { Label = "A" };
  529. var btn2 = new CommandBarButton { Label = "B" };
  530. var btn3 = new CommandBarButton { Label = "C" };
  531. cb.PrimaryCommands!.Add(btn1);
  532. cb.PrimaryCommands!.Add(btn2);
  533. cb.PrimaryCommands!.Add(btn3);
  534. Assert.Equal(new ICommandBarElement[] { btn1, btn2, btn3 }, cb.VisiblePrimaryCommands);
  535. }
  536. [Fact]
  537. public void CommandBarSeparator_CanBeAddedToPrimaryCommands()
  538. {
  539. var cb = new CommandBar();
  540. var sep = new CommandBarSeparator();
  541. cb.PrimaryCommands!.Add(sep);
  542. Assert.Contains(sep, cb.VisiblePrimaryCommands);
  543. }
  544. [Fact]
  545. public void CommandBarToggleButton_CanBeAddedToPrimaryCommands()
  546. {
  547. var cb = new CommandBar();
  548. var toggle = new CommandBarToggleButton { Label = "Bold" };
  549. cb.PrimaryCommands!.Add(toggle);
  550. Assert.Contains(toggle, cb.VisiblePrimaryCommands);
  551. }
  552. }
  553. public class CommandBarLabelPositionTests : ScopedTestBase
  554. {
  555. [Fact]
  556. public void DefaultLabelPosition_Collapsed_SetsIsCompactOnExistingPrimaryButton()
  557. {
  558. var cb = new CommandBar();
  559. var btn = new CommandBarButton();
  560. cb.PrimaryCommands!.Add(btn);
  561. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  562. Assert.True(btn.IsCompact);
  563. }
  564. [Fact]
  565. public void DefaultLabelPosition_Bottom_ClearsIsCompactOnPrimaryButton()
  566. {
  567. var cb = new CommandBar();
  568. var btn = new CommandBarButton();
  569. cb.PrimaryCommands!.Add(btn);
  570. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  571. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Bottom;
  572. Assert.False(btn.IsCompact);
  573. }
  574. [Fact]
  575. public void DefaultLabelPosition_Right_SetsLabelPositionOnPrimaryButton()
  576. {
  577. var cb = new CommandBar();
  578. var btn = new CommandBarButton();
  579. cb.PrimaryCommands!.Add(btn);
  580. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
  581. Assert.Equal(CommandBarDefaultLabelPosition.Right, btn.LabelPosition);
  582. }
  583. [Fact]
  584. public void DefaultLabelPosition_Collapsed_SetsLabelPositionOnPrimaryButton()
  585. {
  586. var cb = new CommandBar();
  587. var btn = new CommandBarButton();
  588. cb.PrimaryCommands!.Add(btn);
  589. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  590. Assert.Equal(CommandBarDefaultLabelPosition.Collapsed, btn.LabelPosition);
  591. }
  592. [Fact]
  593. public void DefaultLabelPosition_Collapsed_PropagatesIsCompactToToggleButton()
  594. {
  595. var cb = new CommandBar();
  596. var toggle = new CommandBarToggleButton();
  597. cb.PrimaryCommands!.Add(toggle);
  598. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  599. Assert.True(toggle.IsCompact);
  600. Assert.Equal(CommandBarDefaultLabelPosition.Collapsed, toggle.LabelPosition);
  601. }
  602. [Fact]
  603. public void DefaultLabelPosition_Right_PropagatesLabelPositionToToggleButton()
  604. {
  605. var cb = new CommandBar();
  606. var toggle = new CommandBarToggleButton();
  607. cb.PrimaryCommands!.Add(toggle);
  608. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
  609. Assert.Equal(CommandBarDefaultLabelPosition.Right, toggle.LabelPosition);
  610. }
  611. [Fact]
  612. public void DefaultLabelPosition_Collapsed_SetsIsCompactOnSeparator()
  613. {
  614. var cb = new CommandBar();
  615. var sep = new CommandBarSeparator();
  616. cb.PrimaryCommands!.Add(sep);
  617. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  618. Assert.True(sep.IsCompact);
  619. }
  620. [Fact]
  621. public void NewPrimaryCommand_GetsCurrentLabelPosition_WhenAlreadyCollapsed()
  622. {
  623. var cb = new CommandBar { DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed };
  624. var btn = new CommandBarButton();
  625. cb.PrimaryCommands!.Add(btn);
  626. Assert.True(btn.IsCompact);
  627. Assert.Equal(CommandBarDefaultLabelPosition.Collapsed, btn.LabelPosition);
  628. }
  629. [Fact]
  630. public void NewPrimaryCommand_GetsCurrentLabelPosition_WhenRight()
  631. {
  632. var cb = new CommandBar { DefaultLabelPosition = CommandBarDefaultLabelPosition.Right };
  633. var btn = new CommandBarButton();
  634. cb.PrimaryCommands!.Add(btn);
  635. Assert.Equal(CommandBarDefaultLabelPosition.Right, btn.LabelPosition);
  636. }
  637. [Fact]
  638. public void DefaultLabelPosition_Collapsed_AppliesToSecondaryCommands()
  639. {
  640. var cb = new CommandBar();
  641. var btn = new CommandBarButton();
  642. cb.SecondaryCommands!.Add(btn);
  643. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  644. Assert.True(btn.IsCompact);
  645. }
  646. [Fact]
  647. public void DefaultLabelPosition_DoesNotClearLabelText()
  648. {
  649. var cb = new CommandBar();
  650. var btn = new CommandBarButton { Label = "Save" };
  651. cb.PrimaryCommands!.Add(btn);
  652. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  653. Assert.Equal("Save", btn.Label);
  654. }
  655. }
  656. public class CommandBarOverflowButtonTests : ScopedTestBase
  657. {
  658. [Fact]
  659. public void OverflowButtonVisibility_Visible_SetsIsOverflowButtonVisibleTrue()
  660. {
  661. var cb = new CommandBar { OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible };
  662. Assert.True(cb.IsOverflowButtonVisible);
  663. }
  664. [Fact]
  665. public void OverflowButtonVisibility_Collapsed_SetsIsOverflowButtonVisibleFalse()
  666. {
  667. var cb = new CommandBar { OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed };
  668. Assert.False(cb.IsOverflowButtonVisible);
  669. }
  670. [Fact]
  671. public void OverflowButtonVisibility_Auto_TrueWhenHasSecondaryCommands()
  672. {
  673. var cb = new CommandBar();
  674. cb.SecondaryCommands!.Add(new CommandBarButton());
  675. Assert.True(cb.IsOverflowButtonVisible);
  676. }
  677. [Fact]
  678. public void OverflowButtonVisibility_Auto_FalseWhenNoSecondaryCommands()
  679. {
  680. var cb = new CommandBar();
  681. Assert.False(cb.IsOverflowButtonVisible);
  682. }
  683. [Fact]
  684. public void OverflowButtonVisibility_Visible_RemainsTrueWithoutSecondary()
  685. {
  686. var cb = new CommandBar { OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible };
  687. Assert.True(cb.IsOverflowButtonVisible);
  688. }
  689. [Fact]
  690. public void OverflowButtonVisibility_Collapsed_RemainsFalseEvenWithSecondary()
  691. {
  692. var cb = new CommandBar { OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed };
  693. cb.SecondaryCommands!.Add(new CommandBarButton());
  694. Assert.False(cb.IsOverflowButtonVisible);
  695. }
  696. [Fact]
  697. public void OverflowButtonVisibility_Auto_FalseAfterSecondaryRemoved()
  698. {
  699. var cb = new CommandBar();
  700. var btn = new CommandBarButton();
  701. cb.SecondaryCommands!.Add(btn);
  702. Assert.True(cb.IsOverflowButtonVisible);
  703. cb.SecondaryCommands!.Remove(btn);
  704. Assert.False(cb.IsOverflowButtonVisible);
  705. }
  706. [Fact]
  707. public void OverflowButtonVisibility_SwitchFromAutoToVisible_ShowsButtonImmediately()
  708. {
  709. var cb = new CommandBar();
  710. Assert.False(cb.IsOverflowButtonVisible);
  711. cb.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible;
  712. Assert.True(cb.IsOverflowButtonVisible);
  713. }
  714. }
  715. public class CommandBarItemWidthTests : ScopedTestBase
  716. {
  717. private static CommandBar CreateWithWidth(double width)
  718. {
  719. var cb = new CommandBar();
  720. cb.Measure(new Size(width, double.PositiveInfinity));
  721. return cb;
  722. }
  723. [Fact]
  724. public void ItemWidthBottom_Controls_HowManyButtonsFit()
  725. {
  726. var cb = CreateWithWidth(300);
  727. var secondary = new CommandBarButton();
  728. cb.SecondaryCommands!.Add(secondary); // forces overflow button
  729. for (int i = 0; i < 4; i++)
  730. cb.PrimaryCommands!.Add(new CommandBarButton());
  731. cb.IsDynamicOverflowEnabled = true;
  732. Assert.Equal(3, cb.VisiblePrimaryCommands.Count);
  733. Assert.Equal(3, cb.OverflowItems.Count);
  734. Assert.IsType<CommandBarButton>(cb.OverflowItems[0]);
  735. Assert.IsType<CommandBarSeparator>(cb.OverflowItems[1]);
  736. Assert.Same(secondary, cb.OverflowItems[2]);
  737. }
  738. [Fact]
  739. public void ItemWidthBottom_Reduced_AllowsMoreItemsToFit()
  740. {
  741. var cb = CreateWithWidth(300);
  742. cb.ItemWidthBottom = 35;
  743. cb.SecondaryCommands!.Add(new CommandBarButton());
  744. for (int i = 0; i < 4; i++)
  745. cb.PrimaryCommands!.Add(new CommandBarButton());
  746. cb.IsDynamicOverflowEnabled = true;
  747. Assert.Equal(4, cb.VisiblePrimaryCommands.Count);
  748. }
  749. [Fact]
  750. public void ItemWidthBottom_Large_ReducesToMinimumOneVisible()
  751. {
  752. var cb = CreateWithWidth(300);
  753. cb.ItemWidthBottom = 260;
  754. cb.SecondaryCommands!.Add(new CommandBarButton());
  755. for (int i = 0; i < 3; i++)
  756. cb.PrimaryCommands!.Add(new CommandBarButton());
  757. cb.IsDynamicOverflowEnabled = true;
  758. Assert.Equal(1, cb.VisiblePrimaryCommands.Count);
  759. }
  760. [Fact]
  761. public void ItemWidthRight_UsedWhenLabelPositionIsRight()
  762. {
  763. var cb = CreateWithWidth(300);
  764. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
  765. cb.SecondaryCommands!.Add(new CommandBarButton());
  766. for (int i = 0; i < 4; i++)
  767. cb.PrimaryCommands!.Add(new CommandBarButton());
  768. cb.IsDynamicOverflowEnabled = true;
  769. Assert.Equal(2, cb.VisiblePrimaryCommands.Count);
  770. }
  771. [Fact]
  772. public void ItemWidthRight_Increased_FewerItemsFit()
  773. {
  774. var cb = CreateWithWidth(300);
  775. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
  776. cb.ItemWidthRight = 252; // exactly 1 fits: 252/252=1
  777. cb.SecondaryCommands!.Add(new CommandBarButton());
  778. for (int i = 0; i < 3; i++)
  779. cb.PrimaryCommands!.Add(new CommandBarButton());
  780. cb.IsDynamicOverflowEnabled = true;
  781. Assert.Equal(1, cb.VisiblePrimaryCommands.Count);
  782. }
  783. [Fact]
  784. public void ItemWidthCollapsed_UsedWhenLabelPositionIsCollapsed()
  785. {
  786. var cb = CreateWithWidth(300);
  787. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  788. cb.SecondaryCommands!.Add(new CommandBarButton());
  789. for (int i = 0; i < 4; i++)
  790. cb.PrimaryCommands!.Add(new CommandBarButton());
  791. cb.IsDynamicOverflowEnabled = true;
  792. Assert.Equal(4, cb.VisiblePrimaryCommands.Count);
  793. }
  794. [Fact]
  795. public void ItemWidths_AreIndependent_PerLabelPosition()
  796. {
  797. var cb = CreateWithWidth(300);
  798. cb.ItemWidthBottom = 70;
  799. cb.ItemWidthRight = 102;
  800. cb.ItemWidthCollapsed = 42;
  801. cb.SecondaryCommands!.Add(new CommandBarButton());
  802. for (int i = 0; i < 4; i++)
  803. cb.PrimaryCommands!.Add(new CommandBarButton());
  804. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Bottom;
  805. cb.IsDynamicOverflowEnabled = true;
  806. int visibleBottom = cb.VisiblePrimaryCommands.Count; // 252/70 = 3
  807. cb.IsDynamicOverflowEnabled = false;
  808. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
  809. cb.IsDynamicOverflowEnabled = true;
  810. int visibleRight = cb.VisiblePrimaryCommands.Count; // 252/102 = 2
  811. cb.IsDynamicOverflowEnabled = false;
  812. cb.DefaultLabelPosition = CommandBarDefaultLabelPosition.Collapsed;
  813. cb.IsDynamicOverflowEnabled = true;
  814. int visibleCollapsed = cb.VisiblePrimaryCommands.Count; // 252/42 = 6 → capped at 4
  815. Assert.Equal(3, visibleBottom);
  816. Assert.Equal(2, visibleRight);
  817. Assert.Equal(4, visibleCollapsed);
  818. }
  819. }
  820. public class CommandBarOverflowKeyboardTests : ScopedTestBase
  821. {
  822. private readonly IDisposable _app;
  823. public CommandBarOverflowKeyboardTests()
  824. {
  825. _app = UnitTestApplication.Start(TestServices.FocusableWindow);
  826. }
  827. public override void Dispose()
  828. {
  829. _app.Dispose();
  830. base.Dispose();
  831. }
  832. [Fact]
  833. public void Escape_WhenOverflowOpen_ClosesOverflow()
  834. {
  835. var cb = new CommandBar();
  836. cb.SecondaryCommands.Add(new CommandBarButton { Label = "Action" });
  837. var root = new TestRoot(useGlobalStyles: true, child: cb);
  838. root.LayoutManager.ExecuteInitialLayoutPass();
  839. cb.IsOpen = true;
  840. RaiseKeyOnOverflowPresenter(cb, Key.Escape);
  841. Assert.False(cb.IsOpen);
  842. }
  843. [Fact]
  844. public void Escape_WhenOverflowClosed_DoesNothing()
  845. {
  846. var cb = new CommandBar();
  847. cb.SecondaryCommands.Add(new CommandBarButton { Label = "Action" });
  848. var root = new TestRoot(useGlobalStyles: true, child: cb);
  849. root.LayoutManager.ExecuteInitialLayoutPass();
  850. RaiseKeyOnOverflowPresenter(cb, Key.Escape);
  851. Assert.False(cb.IsOpen);
  852. }
  853. [Theory]
  854. [InlineData(Key.Down)]
  855. [InlineData(Key.Up)]
  856. [InlineData(Key.Home)]
  857. [InlineData(Key.End)]
  858. public void NavigationKeys_AreHandled_WhenOverflowHasItems(Key key)
  859. {
  860. var cb = new CommandBar();
  861. cb.SecondaryCommands.Add(new CommandBarButton { Label = "A" });
  862. cb.SecondaryCommands.Add(new CommandBarButton { Label = "B" });
  863. var root = new TestRoot(useGlobalStyles: true, child: cb);
  864. root.LayoutManager.ExecuteInitialLayoutPass();
  865. cb.IsOpen = true;
  866. var e = new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key };
  867. GetOverflowPresenter(cb).RaiseEvent(e);
  868. Assert.True(e.Handled);
  869. }
  870. [Fact]
  871. public void NavigationKeys_AreHandled_WhenAllItemsAreSeparators()
  872. {
  873. var cb = new CommandBar();
  874. cb.SecondaryCommands.Add(new CommandBarSeparator());
  875. var root = new TestRoot(useGlobalStyles: true, child: cb);
  876. root.LayoutManager.ExecuteInitialLayoutPass();
  877. cb.IsOpen = true;
  878. var e = new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = Key.Down };
  879. GetOverflowPresenter(cb).RaiseEvent(e);
  880. Assert.True(e.Handled);
  881. }
  882. [Fact]
  883. public void NavigationKeys_AreHandled_WhenAllItemsAreDisabled()
  884. {
  885. var cb = new CommandBar();
  886. cb.SecondaryCommands.Add(new CommandBarButton { Label = "A", IsEnabled = false });
  887. var root = new TestRoot(useGlobalStyles: true, child: cb);
  888. root.LayoutManager.ExecuteInitialLayoutPass();
  889. cb.IsOpen = true;
  890. var e = new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = Key.Down };
  891. GetOverflowPresenter(cb).RaiseEvent(e);
  892. Assert.True(e.Handled);
  893. }
  894. [Fact]
  895. public void NavigationKeys_AreHandled_WhenAllItemsAreNonFocusable()
  896. {
  897. var cb = new CommandBar();
  898. cb.SecondaryCommands.Add(new CommandBarButton { Label = "A", Focusable = false });
  899. var root = new TestRoot(useGlobalStyles: true, child: cb);
  900. root.LayoutManager.ExecuteInitialLayoutPass();
  901. cb.IsOpen = true;
  902. var e = new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = Key.Down };
  903. GetOverflowPresenter(cb).RaiseEvent(e);
  904. Assert.True(e.Handled);
  905. }
  906. [Fact]
  907. public void KeyboardOpen_FocusesFirstOverflowItem_AsFocusVisible()
  908. {
  909. var first = new CommandBarButton { Label = "A" };
  910. var cb = new CommandBar();
  911. cb.SecondaryCommands.Add(first);
  912. var window = CreateWindow(cb);
  913. Assert.True(GetOverflowButton(cb).Focus(NavigationMethod.Tab));
  914. cb.IsOpen = true;
  915. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded, TestContext.Current.CancellationToken);
  916. Assert.True(first.IsFocused);
  917. Assert.True(first.Classes.Contains(":focus-visible"));
  918. }
  919. [Fact]
  920. public void PointerOpen_AfterKeyboardFocus_DoesNotMakeFirstOverflowItemFocusVisible()
  921. {
  922. var first = new CommandBarButton { Label = "A" };
  923. var cb = new CommandBar();
  924. cb.SecondaryCommands.Add(first);
  925. var window = CreateWindow(cb);
  926. var overflowButton = GetOverflowButton(cb);
  927. Assert.True(overflowButton.Focus(NavigationMethod.Tab));
  928. RaisePointerPressed(overflowButton);
  929. cb.IsOpen = true;
  930. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded, TestContext.Current.CancellationToken);
  931. Assert.True(first.IsFocused);
  932. Assert.False(first.Classes.Contains(":focus-visible"));
  933. }
  934. [Fact]
  935. public void KeyboardOpen_AfterPointerFocus_MakesFirstOverflowItemFocusVisible()
  936. {
  937. var first = new CommandBarButton { Label = "A" };
  938. var cb = new CommandBar();
  939. cb.SecondaryCommands.Add(first);
  940. var window = CreateWindow(cb);
  941. var overflowButton = GetOverflowButton(cb);
  942. Assert.True(overflowButton.Focus(NavigationMethod.Pointer));
  943. overflowButton.RaiseEvent(new KeyEventArgs
  944. {
  945. RoutedEvent = InputElement.KeyDownEvent,
  946. Key = Key.Space,
  947. });
  948. cb.IsOpen = true;
  949. Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded, TestContext.Current.CancellationToken);
  950. Assert.True(first.IsFocused);
  951. Assert.True(first.Classes.Contains(":focus-visible"));
  952. }
  953. private static ItemsControl GetOverflowPresenter(CommandBar cb)
  954. {
  955. var popup = cb.GetVisualDescendants()
  956. .OfType<Popup>()
  957. .First(p => p.Name == "PART_OverflowPopup");
  958. return popup.GetLogicalDescendants()
  959. .OfType<ItemsControl>()
  960. .First(x => x.Name == "PART_OverflowPresenter");
  961. }
  962. private static void RaiseKeyOnOverflowPresenter(CommandBar cb, Key key)
  963. {
  964. GetOverflowPresenter(cb).RaiseEvent(new KeyEventArgs
  965. {
  966. RoutedEvent = InputElement.KeyDownEvent,
  967. Key = key,
  968. });
  969. }
  970. private static Button GetOverflowButton(CommandBar cb)
  971. {
  972. return cb.GetVisualDescendants()
  973. .OfType<Button>()
  974. .First(x => x.Name == "PART_OverflowButton");
  975. }
  976. private static Window CreateWindow(Control content)
  977. {
  978. var window = new Window { Content = content };
  979. window.Show();
  980. window.ApplyStyling();
  981. window.ApplyTemplate();
  982. return window;
  983. }
  984. private static void RaisePointerPressed(Button target)
  985. {
  986. var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);
  987. target.RaiseEvent(new PointerPressedEventArgs(
  988. target,
  989. pointer,
  990. target,
  991. default,
  992. timestamp: 1,
  993. new PointerPointProperties(RawInputModifiers.LeftMouseButton, PointerUpdateKind.LeftButtonPressed),
  994. KeyModifiers.None));
  995. }
  996. }
  997. public class CommandBarSeparatorOverflowTests : ScopedTestBase
  998. {
  999. private static CommandBar CreateWithWidth(double width)
  1000. {
  1001. var cb = new CommandBar();
  1002. cb.Measure(new Size(width, double.PositiveInfinity));
  1003. return cb;
  1004. }
  1005. [Fact]
  1006. public void TrailingSeparator_IsNotLastVisibleItem()
  1007. {
  1008. // [Btn, Btn, Sep, Btn] with room for 2 buttons: Sep should NOT trail.
  1009. var cb = CreateWithWidth(300);
  1010. cb.SecondaryCommands!.Add(new CommandBarButton());
  1011. cb.PrimaryCommands!.Add(new CommandBarButton());
  1012. cb.PrimaryCommands.Add(new CommandBarButton());
  1013. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1014. cb.PrimaryCommands.Add(new CommandBarButton());
  1015. cb.IsDynamicOverflowEnabled = true;
  1016. Assert.IsNotType<CommandBarSeparator>(cb.VisiblePrimaryCommands[^1]);
  1017. }
  1018. [Fact]
  1019. public void TrailingSeparator_MovedToOverflow()
  1020. {
  1021. // [Btn, Sep, Btn, Btn] with room for 1 button: Sep after the single visible button should overflow.
  1022. var cb = CreateWithWidth(300);
  1023. cb.ItemWidthBottom = 260;
  1024. cb.SecondaryCommands!.Add(new CommandBarButton());
  1025. cb.PrimaryCommands!.Add(new CommandBarButton());
  1026. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1027. cb.PrimaryCommands.Add(new CommandBarButton());
  1028. cb.PrimaryCommands.Add(new CommandBarButton());
  1029. cb.IsDynamicOverflowEnabled = true;
  1030. Assert.Equal(1, cb.VisiblePrimaryCommands.Count);
  1031. Assert.IsType<CommandBarButton>(cb.VisiblePrimaryCommands[0]);
  1032. }
  1033. [Fact]
  1034. public void MultipleSeparators_AllTrailingOnesStripped()
  1035. {
  1036. // [Btn, Sep, Sep, Btn] with room for 1: both trailing separators should be stripped.
  1037. var cb = CreateWithWidth(300);
  1038. cb.ItemWidthBottom = 260;
  1039. cb.SecondaryCommands!.Add(new CommandBarButton());
  1040. cb.PrimaryCommands!.Add(new CommandBarButton());
  1041. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1042. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1043. cb.PrimaryCommands.Add(new CommandBarButton());
  1044. cb.IsDynamicOverflowEnabled = true;
  1045. Assert.Equal(1, cb.VisiblePrimaryCommands.Count);
  1046. Assert.IsType<CommandBarButton>(cb.VisiblePrimaryCommands[0]);
  1047. }
  1048. [Fact]
  1049. public void MidSeparator_StaysVisible_WhenButtonsOnBothSides()
  1050. {
  1051. // [Btn, Sep, Btn] with room for all: separator stays.
  1052. var cb = CreateWithWidth(300);
  1053. cb.PrimaryCommands!.Add(new CommandBarButton());
  1054. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1055. cb.PrimaryCommands.Add(new CommandBarButton());
  1056. cb.IsDynamicOverflowEnabled = true;
  1057. Assert.Equal(3, cb.VisiblePrimaryCommands.Count);
  1058. Assert.IsType<CommandBarSeparator>(cb.VisiblePrimaryCommands[1]);
  1059. }
  1060. [Fact]
  1061. public void AllButtonsOverflow_SeparatorsAlsoOverflow()
  1062. {
  1063. // [Sep, Btn, Btn] with room for 0: everything overflows.
  1064. var cb = CreateWithWidth(50);
  1065. cb.SecondaryCommands!.Add(new CommandBarButton());
  1066. cb.PrimaryCommands!.Add(new CommandBarSeparator());
  1067. cb.PrimaryCommands.Add(new CommandBarButton());
  1068. cb.PrimaryCommands.Add(new CommandBarButton());
  1069. cb.IsDynamicOverflowEnabled = true;
  1070. Assert.Empty(cb.VisiblePrimaryCommands);
  1071. }
  1072. [Fact]
  1073. public void LeadingSeparator_IsStrippedFromVisible()
  1074. {
  1075. // [Sep, Btn, Btn, Btn] with room for 2: leading Sep should be stripped.
  1076. var cb = CreateWithWidth(300);
  1077. cb.SecondaryCommands!.Add(new CommandBarButton());
  1078. cb.PrimaryCommands!.Add(new CommandBarSeparator());
  1079. cb.PrimaryCommands.Add(new CommandBarButton());
  1080. cb.PrimaryCommands.Add(new CommandBarButton());
  1081. cb.PrimaryCommands.Add(new CommandBarButton());
  1082. cb.IsDynamicOverflowEnabled = true;
  1083. Assert.IsNotType<CommandBarSeparator>(cb.VisiblePrimaryCommands[0]);
  1084. }
  1085. [Fact]
  1086. public void ConsecutiveSeparators_CollapsedToOne()
  1087. {
  1088. // [Btn, Sep, Sep, Btn] all fit: only one separator should remain.
  1089. var cb = CreateWithWidth(300);
  1090. cb.PrimaryCommands!.Add(new CommandBarButton());
  1091. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1092. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1093. cb.PrimaryCommands.Add(new CommandBarButton());
  1094. cb.IsDynamicOverflowEnabled = true;
  1095. int sepCount = CountSeparators(cb.VisiblePrimaryCommands);
  1096. Assert.Equal(1, sepCount);
  1097. }
  1098. [Fact]
  1099. public void OrphanedMidSeparator_RemovedWhenNeighborOverflows()
  1100. {
  1101. // [Btn1, Sep, Btn2, Sep, Btn3] with room for 2: Btn3 overflows,
  1102. // second Sep becomes trailing and is removed. First Sep stays.
  1103. var cb = CreateWithWidth(300);
  1104. cb.ItemWidthBottom = 100;
  1105. cb.SecondaryCommands!.Add(new CommandBarButton());
  1106. cb.PrimaryCommands!.Add(new CommandBarButton());
  1107. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1108. cb.PrimaryCommands.Add(new CommandBarButton());
  1109. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1110. cb.PrimaryCommands.Add(new CommandBarButton());
  1111. cb.IsDynamicOverflowEnabled = true;
  1112. Assert.IsNotType<CommandBarSeparator>(cb.VisiblePrimaryCommands[^1]);
  1113. Assert.Equal(1, CountSeparators(cb.VisiblePrimaryCommands));
  1114. }
  1115. [Fact]
  1116. public void SeparatorBetweenOverflowedButtons_IsRemoved()
  1117. {
  1118. // [Btn1, Btn2, Sep, Btn3, Btn4] with room for 2: Btn3 and Btn4 overflow,
  1119. // Sep has no non-separator after it in visible set, so it is removed.
  1120. var cb = CreateWithWidth(300);
  1121. cb.ItemWidthBottom = 100;
  1122. cb.SecondaryCommands!.Add(new CommandBarButton());
  1123. cb.PrimaryCommands!.Add(new CommandBarButton());
  1124. cb.PrimaryCommands.Add(new CommandBarButton());
  1125. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1126. cb.PrimaryCommands.Add(new CommandBarButton());
  1127. cb.PrimaryCommands.Add(new CommandBarButton());
  1128. cb.IsDynamicOverflowEnabled = true;
  1129. Assert.Equal(0, CountSeparators(cb.VisiblePrimaryCommands));
  1130. }
  1131. [Fact]
  1132. public void MultipleSeparatorGroups_OnlyValidOnesRemain()
  1133. {
  1134. // [Btn, Sep, Btn, Sep, Btn, Sep, Btn] with room for 3:
  1135. // last Btn overflows, last Sep becomes trailing, the rest stay.
  1136. var cb = CreateWithWidth(300);
  1137. cb.SecondaryCommands!.Add(new CommandBarButton());
  1138. cb.PrimaryCommands!.Add(new CommandBarButton());
  1139. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1140. cb.PrimaryCommands.Add(new CommandBarButton());
  1141. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1142. cb.PrimaryCommands.Add(new CommandBarButton());
  1143. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1144. cb.PrimaryCommands.Add(new CommandBarButton());
  1145. cb.IsDynamicOverflowEnabled = true;
  1146. Assert.IsNotType<CommandBarSeparator>(cb.VisiblePrimaryCommands[^1]);
  1147. Assert.IsNotType<CommandBarSeparator>(cb.VisiblePrimaryCommands[0]);
  1148. }
  1149. [Fact]
  1150. public void OnlySeparators_AllOverflow()
  1151. {
  1152. // [Sep, Sep, Sep] with no buttons: all should overflow.
  1153. var cb = CreateWithWidth(300);
  1154. cb.SecondaryCommands!.Add(new CommandBarButton());
  1155. cb.PrimaryCommands!.Add(new CommandBarSeparator());
  1156. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1157. cb.PrimaryCommands.Add(new CommandBarSeparator());
  1158. cb.IsDynamicOverflowEnabled = true;
  1159. Assert.Empty(cb.VisiblePrimaryCommands);
  1160. }
  1161. [Fact]
  1162. public void PrimarySeparator_IsRemovedInsteadOfBecomingFirstOverflowItem()
  1163. {
  1164. var cb = CreateWithWidth(300);
  1165. cb.ItemWidthBottom = 260;
  1166. var leadingSeparator = new CommandBarSeparator();
  1167. var firstButton = new CommandBarButton();
  1168. var overflowedButton = new CommandBarButton();
  1169. cb.PrimaryCommands!.Add(leadingSeparator);
  1170. cb.PrimaryCommands.Add(firstButton);
  1171. cb.PrimaryCommands.Add(overflowedButton);
  1172. cb.IsDynamicOverflowEnabled = true;
  1173. Assert.Single(cb.OverflowItems);
  1174. Assert.Same(overflowedButton, cb.OverflowItems[0]);
  1175. Assert.DoesNotContain(leadingSeparator, cb.OverflowItems);
  1176. }
  1177. [Fact]
  1178. public void OverflowedPrimaryCommands_PrecedeSecondaryCommands_WithSyntheticSeparator()
  1179. {
  1180. var cb = CreateWithWidth(300);
  1181. cb.ItemWidthBottom = 260;
  1182. var visiblePrimary = new CommandBarButton();
  1183. var originalPrimarySeparator = new CommandBarSeparator();
  1184. var overflowedPrimaryOne = new CommandBarButton();
  1185. var overflowedPrimaryTwo = new CommandBarButton();
  1186. var secondary = new CommandBarButton();
  1187. cb.PrimaryCommands!.Add(visiblePrimary);
  1188. cb.PrimaryCommands.Add(originalPrimarySeparator);
  1189. cb.PrimaryCommands.Add(overflowedPrimaryOne);
  1190. cb.PrimaryCommands.Add(overflowedPrimaryTwo);
  1191. cb.SecondaryCommands!.Add(secondary);
  1192. cb.IsDynamicOverflowEnabled = true;
  1193. Assert.Equal(4, cb.OverflowItems.Count);
  1194. Assert.Same(overflowedPrimaryOne, cb.OverflowItems[0]);
  1195. Assert.Same(overflowedPrimaryTwo, cb.OverflowItems[1]);
  1196. Assert.IsType<CommandBarSeparator>(cb.OverflowItems[2]);
  1197. Assert.NotSame(originalPrimarySeparator, cb.OverflowItems[2]);
  1198. Assert.Same(secondary, cb.OverflowItems[3]);
  1199. Assert.DoesNotContain(originalPrimarySeparator, cb.OverflowItems);
  1200. }
  1201. [Fact]
  1202. public void HiddenSecondaryCommands_DoNotGetSyntheticOverflowSeparator()
  1203. {
  1204. var cb = CreateWithWidth(300);
  1205. cb.ItemWidthBottom = 260;
  1206. var visiblePrimary = new CommandBarButton();
  1207. var overflowedPrimary = new CommandBarButton();
  1208. var hiddenSecondary = new CommandBarButton { IsVisible = false };
  1209. cb.PrimaryCommands!.Add(visiblePrimary);
  1210. cb.PrimaryCommands.Add(overflowedPrimary);
  1211. cb.SecondaryCommands!.Add(hiddenSecondary);
  1212. cb.IsDynamicOverflowEnabled = true;
  1213. Assert.Equal(2, cb.OverflowItems.Count);
  1214. Assert.Same(overflowedPrimary, cb.OverflowItems[0]);
  1215. Assert.Same(hiddenSecondary, cb.OverflowItems[1]);
  1216. Assert.DoesNotContain(cb.OverflowItems, x => x is CommandBarSeparator);
  1217. }
  1218. [Fact]
  1219. public void TogglingSecondaryVisibility_RebuildsSyntheticOverflowSeparator()
  1220. {
  1221. var cb = CreateWithWidth(300);
  1222. cb.ItemWidthBottom = 260;
  1223. var visiblePrimary = new CommandBarButton();
  1224. var overflowedPrimary = new CommandBarButton();
  1225. var secondary = new CommandBarButton();
  1226. cb.PrimaryCommands!.Add(visiblePrimary);
  1227. cb.PrimaryCommands.Add(overflowedPrimary);
  1228. cb.SecondaryCommands!.Add(secondary);
  1229. cb.IsDynamicOverflowEnabled = true;
  1230. Assert.Equal(3, cb.OverflowItems.Count);
  1231. Assert.Same(overflowedPrimary, cb.OverflowItems[0]);
  1232. Assert.IsType<CommandBarSeparator>(cb.OverflowItems[1]);
  1233. Assert.Same(secondary, cb.OverflowItems[2]);
  1234. secondary.IsVisible = false;
  1235. Assert.Equal(2, cb.OverflowItems.Count);
  1236. Assert.Same(overflowedPrimary, cb.OverflowItems[0]);
  1237. Assert.Same(secondary, cb.OverflowItems[1]);
  1238. Assert.DoesNotContain(cb.OverflowItems, x => x is CommandBarSeparator);
  1239. secondary.IsVisible = true;
  1240. Assert.Equal(3, cb.OverflowItems.Count);
  1241. Assert.Same(overflowedPrimary, cb.OverflowItems[0]);
  1242. Assert.IsType<CommandBarSeparator>(cb.OverflowItems[1]);
  1243. Assert.Same(secondary, cb.OverflowItems[2]);
  1244. }
  1245. private static int CountSeparators(IReadOnlyList<ICommandBarElement> items)
  1246. {
  1247. int count = 0;
  1248. for (var i = 0; i < items.Count; i++)
  1249. {
  1250. if (items[i] is CommandBarSeparator)
  1251. count++;
  1252. }
  1253. return count;
  1254. }
  1255. }
  1256. file sealed class DelegateCommand : System.Windows.Input.ICommand
  1257. {
  1258. private readonly System.Action<object?> _execute;
  1259. private readonly System.Func<object?, bool> _canExecute;
  1260. public DelegateCommand(System.Action<object?> execute, System.Func<object?, bool>? canExecute = null)
  1261. {
  1262. _execute = execute;
  1263. _canExecute = canExecute ?? (_ => true);
  1264. }
  1265. public event System.EventHandler? CanExecuteChanged { add { } remove { } }
  1266. public bool CanExecute(object? parameter) => _canExecute(parameter);
  1267. public void Execute(object? parameter) => _execute(parameter);
  1268. }