PopupTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Specialized;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using Moq;
  8. using Avalonia.Controls.Presenters;
  9. using Avalonia.Controls.Primitives;
  10. using Avalonia.Controls.Templates;
  11. using Avalonia.Layout;
  12. using Avalonia.LogicalTree;
  13. using Avalonia.Platform;
  14. using Avalonia.Styling;
  15. using Avalonia.UnitTests;
  16. using Avalonia.VisualTree;
  17. using Xunit;
  18. using Avalonia.Input;
  19. namespace Avalonia.Controls.UnitTests.Primitives
  20. {
  21. public class PopupTests
  22. {
  23. protected bool UsePopupHost;
  24. [Fact]
  25. public void Setting_Child_Should_Set_Child_Controls_LogicalParent()
  26. {
  27. var target = new Popup();
  28. var child = new Control();
  29. target.Child = child;
  30. Assert.Equal(child.Parent, target);
  31. Assert.Equal(((ILogical)child).LogicalParent, target);
  32. }
  33. [Fact]
  34. public void Clearing_Child_Should_Clear_Child_Controls_Parent()
  35. {
  36. var target = new Popup();
  37. var child = new Control();
  38. target.Child = child;
  39. target.Child = null;
  40. Assert.Null(child.Parent);
  41. Assert.Null(((ILogical)child).LogicalParent);
  42. }
  43. [Fact]
  44. public void Child_Control_Should_Appear_In_LogicalChildren()
  45. {
  46. var target = new Popup();
  47. var child = new Control();
  48. target.Child = child;
  49. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  50. }
  51. [Fact]
  52. public void Clearing_Child_Should_Remove_From_LogicalChildren()
  53. {
  54. var target = new Popup();
  55. var child = new Control();
  56. target.Child = child;
  57. target.Child = null;
  58. Assert.Equal(new ILogical[0], ((ILogical)target).LogicalChildren.ToList());
  59. }
  60. [Fact]
  61. public void Setting_Child_Should_Fire_LogicalChildren_CollectionChanged()
  62. {
  63. var target = new Popup();
  64. var child = new Control();
  65. var called = false;
  66. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  67. called = e.Action == NotifyCollectionChangedAction.Add;
  68. target.Child = child;
  69. Assert.True(called);
  70. }
  71. [Fact]
  72. public void Clearing_Child_Should_Fire_LogicalChildren_CollectionChanged()
  73. {
  74. var target = new Popup();
  75. var child = new Control();
  76. var called = false;
  77. target.Child = child;
  78. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  79. called = e.Action == NotifyCollectionChangedAction.Remove;
  80. target.Child = null;
  81. Assert.True(called);
  82. }
  83. [Fact]
  84. public void Changing_Child_Should_Fire_LogicalChildren_CollectionChanged()
  85. {
  86. var target = new Popup();
  87. var child1 = new Control();
  88. var child2 = new Control();
  89. var called = false;
  90. target.Child = child1;
  91. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
  92. target.Child = child2;
  93. Assert.True(called);
  94. }
  95. [Fact]
  96. public void Setting_Child_Should_Not_Set_Childs_VisualParent()
  97. {
  98. var target = new Popup();
  99. var child = new Control();
  100. target.Child = child;
  101. Assert.Null(((IVisual)child).VisualParent);
  102. }
  103. [Fact]
  104. public void PopupRoot_Should_Initially_Be_Null()
  105. {
  106. using (CreateServices())
  107. {
  108. var target = new Popup();
  109. Assert.Null(((Visual)target.Host));
  110. }
  111. }
  112. [Fact]
  113. public void PopupRoot_Should_Have_Popup_As_LogicalParent()
  114. {
  115. using (CreateServices())
  116. {
  117. var target = new Popup() {PlacementTarget = PreparedWindow()};
  118. target.Open();
  119. Assert.Equal(target, ((Visual)target.Host).Parent);
  120. Assert.Equal(target, ((Visual)target.Host).GetLogicalParent());
  121. }
  122. }
  123. [Fact]
  124. public void PopupRoot_Should_Be_Detached_From_Logical_Tree_When_Popup_Is_Detached()
  125. {
  126. using (CreateServices())
  127. {
  128. var target = new Popup() {PlacementMode = PlacementMode.Pointer};
  129. var root = PreparedWindow(target);
  130. target.Open();
  131. var popupRoot = (ILogical)((Visual)target.Host);
  132. Assert.True(popupRoot.IsAttachedToLogicalTree);
  133. root.Content = null;
  134. Assert.False(((ILogical)target).IsAttachedToLogicalTree);
  135. }
  136. }
  137. [Fact]
  138. public void Popup_Open_Should_Raise_Single_Opened_Event()
  139. {
  140. using (CreateServices())
  141. {
  142. var window = PreparedWindow();
  143. var target = new Popup() {PlacementMode = PlacementMode.Pointer};
  144. window.Content = target;
  145. int openedCount = 0;
  146. target.Opened += (sender, args) =>
  147. {
  148. openedCount++;
  149. };
  150. target.Open();
  151. Assert.Equal(1, openedCount);
  152. }
  153. }
  154. [Fact]
  155. public void Popup_Close_Should_Raise_Single_Closed_Event()
  156. {
  157. using (CreateServices())
  158. {
  159. var window = PreparedWindow();
  160. var target = new Popup() {PlacementMode = PlacementMode.Pointer};
  161. window.Content = target;
  162. window.ApplyTemplate();
  163. target.Open();
  164. int closedCount = 0;
  165. target.Closed += (sender, args) =>
  166. {
  167. closedCount++;
  168. };
  169. target.Close();
  170. Assert.Equal(1, closedCount);
  171. }
  172. }
  173. [Fact]
  174. public void Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent()
  175. {
  176. using (CreateServices())
  177. {
  178. PopupContentControl target;
  179. var root = PreparedWindow(target = new PopupContentControl
  180. {
  181. Content = new Border(),
  182. Template = new FuncControlTemplate<PopupContentControl>(PopupContentControlTemplate),
  183. });
  184. root.Show();
  185. target.ApplyTemplate();
  186. var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
  187. popup.Open();
  188. var popupRoot = (Control)popup.Host;
  189. popupRoot.Measure(Size.Infinity);
  190. popupRoot.Arrange(new Rect(popupRoot.DesiredSize));
  191. var children = popupRoot.GetVisualDescendants().ToList();
  192. var types = children.Select(x => x.GetType().Name).ToList();
  193. Assert.Equal(
  194. new[]
  195. {
  196. "ContentPresenter",
  197. "ContentPresenter",
  198. "Border",
  199. },
  200. types);
  201. var templatedParents = children
  202. .OfType<IControl>()
  203. .Select(x => x.TemplatedParent).ToList();
  204. Assert.Equal(
  205. new object[]
  206. {
  207. popupRoot,
  208. target,
  209. null,
  210. },
  211. templatedParents);
  212. }
  213. }
  214. Window PreparedWindow(object content = null)
  215. {
  216. var w = new Window {Content = content};
  217. w.ApplyTemplate();
  218. return w;
  219. }
  220. [Fact]
  221. public void DataContextBeginUpdate_Should_Not_Be_Called_For_Controls_That_Dont_Inherit()
  222. {
  223. using (CreateServices())
  224. {
  225. TestControl child;
  226. var popup = new Popup
  227. {
  228. Child = child = new TestControl(),
  229. DataContext = "foo",
  230. PlacementTarget = PreparedWindow()
  231. };
  232. var beginCalled = false;
  233. child.DataContextBeginUpdate += (s, e) => beginCalled = true;
  234. // Test for #1245. Here, the child's logical parent is the popup but it's not yet
  235. // attached to a visual tree because the popup hasn't been opened.
  236. Assert.Same(popup, ((ILogical)child).LogicalParent);
  237. Assert.Same(popup, child.InheritanceParent);
  238. Assert.Null(child.GetVisualRoot());
  239. popup.Open();
  240. // #1245 was caused by the fact that DataContextBeginUpdate was called on `target`
  241. // when the PopupRoot was created, even though PopupRoot isn't the
  242. // InheritanceParent of child.
  243. Assert.False(beginCalled);
  244. }
  245. }
  246. [Fact]
  247. public void Popup_Host_Type_Should_Match_Platform_Preference()
  248. {
  249. using (CreateServices())
  250. {
  251. var target = new Popup() {PlacementTarget = PreparedWindow()};
  252. target.Open();
  253. if (UsePopupHost)
  254. Assert.IsType<PopupHost>(target.Host);
  255. else
  256. Assert.IsType<PopupRoot>(target.Host);
  257. }
  258. }
  259. private IDisposable CreateServices()
  260. {
  261. return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform:
  262. new MockWindowingPlatform(null,
  263. () =>
  264. {
  265. if(UsePopupHost)
  266. return null;
  267. return MockWindowingPlatform.CreatePopupMock().Object;
  268. })));
  269. }
  270. private static IControl PopupContentControlTemplate(PopupContentControl control, INameScope scope)
  271. {
  272. return new Popup
  273. {
  274. Name = "popup",
  275. PlacementTarget = control,
  276. Child = new ContentPresenter
  277. {
  278. [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty],
  279. }
  280. }.RegisterInNameScope(scope);
  281. }
  282. private class PopupContentControl : ContentControl
  283. {
  284. }
  285. private class TestControl : Decorator
  286. {
  287. public event EventHandler DataContextBeginUpdate;
  288. public new IAvaloniaObject InheritanceParent => base.InheritanceParent;
  289. protected override void OnDataContextBeginUpdate()
  290. {
  291. DataContextBeginUpdate?.Invoke(this, EventArgs.Empty);
  292. base.OnDataContextBeginUpdate();
  293. }
  294. }
  295. }
  296. public class PopupTestsWithPopupRoot : PopupTests
  297. {
  298. public PopupTestsWithPopupRoot()
  299. {
  300. UsePopupHost = true;
  301. }
  302. }
  303. }