diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 58b4324a3e..ca2ed2590f 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -90,6 +90,8 @@ namespace Avalonia.Controls
/// The control.
public void Open(Control control)
{
+ if (control == null)
+ throw new ArgumentNullException(nameof(control));
if (IsOpen)
{
return;
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index f9ec9796fb..2163e035dc 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -215,13 +215,21 @@ namespace Avalonia.Controls.Primitives
///
public void Open()
{
- if (PlacementTarget == null)
- throw new InvalidOperationException("It's not valid to show a popup without a PlacementTarget");
-
+ if (PlacementTarget == null && PlacementMode != PlacementMode.Pointer)
+ throw new InvalidOperationException("It's not valid to show a popup without a PlacementTarget with PlacementMode != Pointer");
if (_topLevel == null && PlacementTarget != null)
- _topLevel = PlacementTarget.GetSelfAndLogicalAncestors().First(x => x is TopLevel) as TopLevel;
-
+ _topLevel = PlacementTarget.GetSelfAndLogicalAncestors().FirstOrDefault(x => x is TopLevel) as TopLevel;
+
+ if (_topLevel == null)
+ {
+ if (PlacementTarget == null)
+ throw new InvalidOperationException(
+ "Attempted to open a popup not attached to a TopLevel and PlacementTarget is null");
+ throw new InvalidOperationException(
+ "Attempted to open a popup not attached to a TopLevel and PlacementTarget is also not attached to a TopLevel");
+ }
+
if (_popupRoot == null)
{
_popupRoot = new PopupRoot(_topLevel, DependencyResolver)
@@ -255,7 +263,7 @@ namespace Avalonia.Controls.Primitives
}
}
_topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
- _nonClientListener = InputManager.Instance.Process.Subscribe(ListenForNonClientClick);
+ _nonClientListener = InputManager.Instance?.Process.Subscribe(ListenForNonClientClick);
PopupRootCreated?.Invoke(this, EventArgs.Empty);
diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs
index efe4d09b3d..d3dba6c908 100644
--- a/src/Avalonia.Controls/Primitives/PopupRoot.cs
+++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs
@@ -146,7 +146,11 @@ namespace Avalonia.Controls.Primitives
throw new InvalidOperationException("Placement mode is not Pointer and PlacementTarget is null");
var matrix = target.TransformToVisual(_parent);
if (matrix == null)
+ {
+ if (target.GetVisualRoot() == null)
+ throw new InvalidCastException("Target control is not attached to the visual tree");
throw new InvalidCastException("Target control is not in the same tree as the popup parent");
+ }
_positionerParameters.AnchorRectangle = new Rect(default, target.Bounds.Size)
.TransformToAABB(matrix.Value);
diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
index 015a122677..ef7dc33f76 100644
--- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
@@ -982,6 +982,8 @@ namespace Avalonia.Controls.UnitTests
AutoCompleteBox control = CreateControl();
control.Items = CreateSimpleStringArray();
TextBox textBox = GetTextBox(control);
+ var window = new Window {Content = control};
+ window.ApplyTemplate();
Dispatcher.UIThread.RunJobs();
test.Invoke(control, textBox);
}
@@ -1027,7 +1029,8 @@ namespace Avalonia.Controls.UnitTests
var popup =
new Popup
{
- Name = "PART_Popup"
+ Name = "PART_Popup",
+ PlacementTarget = control
}.RegisterInNameScope(scope);
var panel = new Panel();
diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
index 58d205deaa..93db620702 100644
--- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
@@ -27,7 +27,7 @@ namespace Avalonia.Controls.UnitTests
ContextMenu = sut
};
- new Window { Content = target };
+ new Window { Content = target }.ApplyTemplate();
int openedCount = 0;
@@ -36,7 +36,7 @@ namespace Avalonia.Controls.UnitTests
openedCount++;
};
- sut.Open(null);
+ sut.Open(target);
Assert.Equal(1, openedCount);
}
@@ -53,9 +53,9 @@ namespace Avalonia.Controls.UnitTests
ContextMenu = sut
};
- new Window { Content = target };
+ new Window { Content = target }.ApplyTemplate();
- sut.Open(null);
+ sut.Open(target);
int closedCount = 0;
@@ -190,12 +190,12 @@ namespace Avalonia.Controls.UnitTests
screenImpl.Setup(x => x.ScreenCount).Returns(1);
screenImpl.Setup(X => X.AllScreens).Returns( new[] { new Screen(screen, screen, true) });
- var windowImpl = new Mock();
- windowImpl.Setup(x => x.Screen).Returns(screenImpl.Object);
-
- popupImpl = new Mock();
+ popupImpl = MockWindowingPlatform.CreatePopupMock();
popupImpl.SetupGet(x => x.Scaling).Returns(1);
+ var windowImpl = MockWindowingPlatform.CreateWindowMock(() => popupImpl.Object);
+ windowImpl.Setup(x => x.Screen).Returns(screenImpl.Object);
+
var services = TestServices.StyledWindow.With(
inputManager: new InputManager(),
windowImpl: windowImpl.Object,
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
index 44bb7cb69b..944bf1e642 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
@@ -43,13 +43,14 @@ namespace Avalonia.Controls.UnitTests.Primitives
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
+ var window = new Window();
var target = new TemplatedControlWithPopup
{
PopupContent = new Canvas(),
};
+ window.Content = target;
- var root = new TestRoot { Child = target };
-
+ window.ApplyTemplate();
target.ApplyTemplate();
target.Popup.Open();
@@ -117,13 +118,14 @@ namespace Avalonia.Controls.UnitTests.Primitives
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
+ var window = new Window();
var target = new TemplatedControlWithPopup
{
PopupContent = new Canvas(),
};
+ window.Content = target;
- var root = new TestRoot { Child = target };
-
+ window.ApplyTemplate();
target.ApplyTemplate();
target.Popup.Open();
target.PopupContent = null;
@@ -158,6 +160,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
new Popup
{
[!Popup.ChildProperty] = parent[!TemplatedControlWithPopup.PopupContentProperty],
+ PlacementTarget = parent
});
}
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index 2e22725125..82610c91df 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -146,7 +146,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
{
using (CreateServices())
{
- var target = new Popup();
+ var target = new Popup() {PlacementTarget = new Window()};
target.Open();
@@ -159,7 +159,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
{
using (CreateServices())
{
- var target = new Popup();
+ var target = new Popup() {PlacementTarget = new Window()};
target.Open();
@@ -173,15 +173,15 @@ namespace Avalonia.Controls.UnitTests.Primitives
{
using (CreateServices())
{
- var target = new Popup();
- var root = new TestRoot { Child = target };
+ var target = new Popup() {PlacementMode = PlacementMode.Pointer};
+ var root = new Window() { Content = target };
target.Open();
var popupRoot = (ILogical)target.PopupRoot;
Assert.True(popupRoot.IsAttachedToLogicalTree);
- root.Child = null;
+ root.Content = null;
Assert.False(((ILogical)target).IsAttachedToLogicalTree);
}
}
@@ -192,7 +192,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
using (CreateServices())
{
var window = new Window();
- var target = new Popup();
+ var target = new Popup() {PlacementMode = PlacementMode.Pointer};
window.Content = target;
@@ -215,9 +215,10 @@ namespace Avalonia.Controls.UnitTests.Primitives
using (CreateServices())
{
var window = new Window();
- var target = new Popup();
+ var target = new Popup() {PlacementMode = PlacementMode.Pointer};
window.Content = target;
+ window.ApplyTemplate();
target.Open();
int closedCount = 0;
@@ -239,10 +240,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
using (CreateServices())
{
var window = new Window();
- var target = new Popup();
+ var target = new Popup {PlacementMode = PlacementMode.Pointer};
var child = new Control();
window.Content = target;
+ window.ApplyTemplate();
target.Open();
Assert.Single(target.PopupRoot.GetVisualChildren());
@@ -259,15 +261,16 @@ namespace Avalonia.Controls.UnitTests.Primitives
using (CreateServices())
{
PopupContentControl target;
- var root = new TestRoot
+ var root = new Window()
{
- Child = target = new PopupContentControl
+ Content = target = new PopupContentControl
{
Content = new Border(),
Template = new FuncControlTemplate(PopupContentControlTemplate),
},
- StylingParent = AvaloniaLocator.Current.GetService()
+ //StylingParent = AvaloniaLocator.Current.GetService()
};
+ root.ApplyTemplate();
target.ApplyTemplate();
var popup = (Popup)target.GetTemplateChildren().First(x => x.Name == "popup");
@@ -311,6 +314,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
{
Child = child = new TestControl(),
DataContext = "foo",
+ PlacementTarget = new Window()
};
var beginCalled = false;
@@ -332,36 +336,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
- private static IDisposable CreateServices()
- {
- var result = AvaloniaLocator.EnterScope();
-
- var styles = new Styles
- {
- new Style(x => x.OfType())
- {
- Setters = new[]
- {
- new Setter(TemplatedControl.TemplateProperty, new FuncControlTemplate(PopupRootTemplate)),
- }
- },
- };
-
- var globalStyles = new Mock();
- globalStyles.Setup(x => x.IsStylesInitialized).Returns(true);
- globalStyles.Setup(x => x.Styles).Returns(styles);
-
- var renderInterface = new Mock();
-
- AvaloniaLocator.CurrentMutable
- .Bind().ToFunc(() => globalStyles.Object)
- .Bind().ToConstant(new WindowingPlatformMock())
- .Bind().ToTransient()
- .Bind().ToFunc(() => renderInterface.Object)
- .Bind().ToConstant(new InputManager());
-
- return result;
- }
+ private static IDisposable CreateServices() => UnitTestApplication.Start(TestServices.StyledWindow);
private static IControl PopupRootTemplate(PopupRoot control, INameScope scope)
{
@@ -377,6 +352,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
return new Popup
{
Name = "popup",
+ PlacementTarget = control,
Child = new ContentPresenter
{
[~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty],
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index cbcf08049e..75239f014f 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -17,31 +17,6 @@ namespace Avalonia.Controls.UnitTests
{
public class WindowTests
{
- [Fact]
- public void Impl_ClientSize_Should_Be_Set_After_Layout_Pass()
- {
- using (UnitTestApplication.Start(TestServices.StyledWindow))
- {
- var impl = Mock.Of(x => x.Scaling == 1);
-
- Mock.Get(impl).Setup(x => x.Resize(It.IsAny())).Callback(() => { });
-
- var target = new Window(impl)
- {
- Content = new TextBlock
- {
- Width = 321,
- Height = 432,
- },
- IsVisible = true,
- };
-
- target.LayoutManager.ExecuteInitialLayoutPass(target);
-
- Mock.Get(impl).Verify(x => x.Resize(new Size(321, 432)));
- }
- }
-
[Fact]
public void Setting_Title_Should_Set_Impl_Title()
{
@@ -302,8 +277,7 @@ namespace Avalonia.Controls.UnitTests
var screens = new Mock();
screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object, screen2.Object });
- var windowImpl = new Mock();
- windowImpl.SetupProperty(x => x.Position);
+ var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));
windowImpl.Setup(x => x.Scaling).Returns(1);
windowImpl.Setup(x => x.Screen).Returns(screens.Object);
@@ -327,14 +301,12 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Window_Should_Be_Centered_Relative_To_Owner_When_WindowStartupLocation_Is_CenterOwner()
{
- var parentWindowImpl = new Mock();
- parentWindowImpl.SetupProperty(x => x.Position);
+ var parentWindowImpl = MockWindowingPlatform.CreateWindowMock();
parentWindowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));
parentWindowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1920, 1080));
parentWindowImpl.Setup(x => x.Scaling).Returns(1);
- var windowImpl = new Mock();
- windowImpl.SetupProperty(x => x.Position);
+ var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.ClientSize).Returns(new Size(320, 200));
windowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1920, 1080));
windowImpl.Setup(x => x.Scaling).Returns(1);
diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
index f065fcb63d..a1b3ab9736 100644
--- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
+++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
@@ -4,6 +4,7 @@
false
Library
false
+ latest
diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs
index 36297bf58b..1b47318fe1 100644
--- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs
+++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs
@@ -1,4 +1,6 @@
using System;
+using Avalonia.Controls.Primitives.PopupPositioning;
+using Avalonia.Input;
using Moq;
using Avalonia.Platform;
@@ -15,16 +17,46 @@ namespace Avalonia.UnitTests
_popupImpl = popupImpl;
}
+ public static Mock CreateWindowMock(Func popupImpl = null)
+ {
+ var win = Mock.Of(x => x.Scaling == 1);
+ var mock = Mock.Get(win);
+ mock.Setup(x => x.CreatePopup()).Returns(() =>
+ {
+ return popupImpl?.Invoke() ?? CreatePopupMock().Object;
+
+ });
+ PixelPoint pos = default;
+ mock.SetupGet(x => x.Position).Returns(() => pos);
+ mock.Setup(x => x.Move(It.IsAny())).Callback(new Action(np => pos = np));
+ SetupToplevel(mock);
+ return mock;
+ }
+
+ static void SetupToplevel(Mock mock) where T : class, ITopLevelImpl
+ {
+ mock.SetupGet(x => x.MouseDevice).Returns(new MouseDevice());
+ }
+
+ public static Mock CreatePopupMock()
+ {
+ var positioner = Mock.Of();
+ var popup = Mock.Of(x => x.Scaling == 1);
+ var mock = Mock.Get(popup);
+ mock.SetupGet(x => x.PopupPositioner).Returns(positioner);
+ SetupToplevel(mock);
+
+ return mock;
+ }
+
public IWindowImpl CreateWindow()
{
- return _windowImpl?.Invoke() ?? Mock.Of(x => x.Scaling == 1);
+ return _windowImpl?.Invoke() ?? CreateWindowMock(_popupImpl).Object;
}
public IEmbeddableWindowImpl CreateEmbeddableWindow()
{
throw new NotImplementedException();
}
-
- public IPopupImpl CreatePopup() => _popupImpl?.Invoke() ?? Mock.Of(x => x.Scaling == 1);
}
-}
\ No newline at end of file
+}