diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 4c84d32637..4546a1aadb 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -117,20 +117,14 @@ namespace Avalonia.Controls.Primitives }); } - /// - /// Carries out the arrange pass of the window. - /// - /// The final window size. - /// The parameter unchanged. - protected override Size ArrangeOverride(Size finalSize) + protected override sealed Size ArrangeSetBounds(Size size) { using (BeginAutoSizing()) { - _positionerParameters.Size = finalSize; + _positionerParameters.Size = size; UpdatePosition(); + return ClientSize; } - - return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); } } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 387bf0adb8..dcf4e98528 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -313,22 +313,7 @@ namespace Avalonia.Controls /// Should be called from left mouse button press event handler /// public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => PlatformImpl?.BeginResizeDrag(edge, e); - - /// - /// Carries out the arrange pass of the window. - /// - /// The final window size. - /// The parameter unchanged. - protected override Size ArrangeOverride(Size finalSize) - { - using (BeginAutoSizing()) - { - PlatformImpl?.Resize(finalSize); - } - return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); - } - /// Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize; @@ -450,6 +435,19 @@ namespace Avalonia.Controls EnsureInitialized(); IsVisible = true; + + var initialSize = new Size( + double.IsNaN(Width) ? ClientSize.Width : Width, + double.IsNaN(Height) ? ClientSize.Height : Height); + + if (initialSize != ClientSize) + { + using (BeginAutoSizing()) + { + PlatformImpl?.Resize(initialSize); + } + } + LayoutManager.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) @@ -569,31 +567,30 @@ namespace Avalonia.Controls } } - /// protected override Size MeasureOverride(Size availableSize) { var sizeToContent = SizeToContent; var clientSize = ClientSize; - var constraint = availableSize; + var constraint = clientSize; - if ((sizeToContent & SizeToContent.Width) != 0) + if (sizeToContent.HasFlagCustom(SizeToContent.Width)) { constraint = constraint.WithWidth(double.PositiveInfinity); } - if ((sizeToContent & SizeToContent.Height) != 0) + if (sizeToContent.HasFlagCustom(SizeToContent.Height)) { constraint = constraint.WithHeight(double.PositiveInfinity); } var result = base.MeasureOverride(constraint); - if ((sizeToContent & SizeToContent.Width) == 0) + if (!sizeToContent.HasFlagCustom(SizeToContent.Width)) { result = result.WithWidth(clientSize.Width); } - if ((sizeToContent & SizeToContent.Height) == 0) + if (!sizeToContent.HasFlagCustom(SizeToContent.Height)) { result = result.WithHeight(clientSize.Height); } @@ -601,6 +598,15 @@ namespace Avalonia.Controls return result; } + protected sealed override Size ArrangeSetBounds(Size size) + { + using (BeginAutoSizing()) + { + PlatformImpl?.Resize(size); + return ClientSize; + } + } + protected sealed override void HandleClosed() { RaiseEvent(new RoutedEventArgs(WindowClosedEvent)); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 63eabb32f4..025dfde610 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -224,16 +224,66 @@ namespace Avalonia.Controls /// The new client size. protected override void HandleResized(Size clientSize) { - if (!AutoSizing) - { - Width = clientSize.Width; - Height = clientSize.Height; - } + Width = clientSize.Width; + Height = clientSize.Height; ClientSize = clientSize; LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } + /// + /// Overrides the core measure logic for windows. + /// + /// The available size. + /// The measured size. + /// + /// The layout logic for top-level windows is different than for other controls because + /// they don't have a parent, meaning that many layout properties handled by the default + /// MeasureCore (such as margins and alignment) make no sense. + /// + protected override Size MeasureCore(Size availableSize) + { + ApplyStyling(); + ApplyTemplate(); + + var constraint = availableSize; + + if (!double.IsNaN(Width)) + { + constraint = constraint.WithWidth(Width); + } + + if (!double.IsNaN(Height)) + { + constraint = constraint.WithHeight(Height); + } + + return MeasureOverride(constraint); + } + + /// + /// Overrides the core arrange logic for windows. + /// + /// The final arrange rect. + /// + /// The layout logic for top-level windows is different than for other controls because + /// they don't have a parent, meaning that many layout properties handled by the default + /// ArrangeCore (such as margins and alignment) make no sense. + /// + protected override void ArrangeCore(Rect finalRect) + { + var constraint = ArrangeSetBounds(finalRect.Size); + var arrangeSize = ArrangeOverride(constraint); + Bounds = new Rect(arrangeSize); + } + + /// + /// Called durung the arrange pass to set the size of the window. + /// + /// The requested size of the window. + /// The actual size of the window. + protected virtual Size ArrangeSetBounds(Size size) => size; + /// /// Handles a window position change notification from /// . diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index 5a47a86e51..28e87dd671 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -209,16 +209,17 @@ namespace Avalonia.Controls.UnitTests screenImpl.Setup(x => x.ScreenCount).Returns(1); screenImpl.Setup(X => X.AllScreens).Returns( new[] { new Screen(1, screen, screen, true) }); - popupImpl = MockWindowingPlatform.CreatePopupMock(); + var windowImpl = MockWindowingPlatform.CreateWindowMock(); + popupImpl = MockWindowingPlatform.CreatePopupMock(windowImpl.Object); popupImpl.SetupGet(x => x.Scaling).Returns(1); + windowImpl.Setup(x => x.CreatePopup()).Returns(popupImpl.Object); - 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, - windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object, () => popupImpl.Object)); + windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object, x => popupImpl.Object)); return UnitTestApplication.Start(services); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index 501c0455d0..b03f8b8892 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.LogicalTree; +using Avalonia.Platform; using Avalonia.Styling; using Avalonia.UnitTests; using Avalonia.VisualTree; @@ -172,9 +173,75 @@ namespace Avalonia.Controls.UnitTests.Primitives } } - private PopupRoot CreateTarget(TopLevel popupParent) + [Fact] + public void Child_Should_Be_Measured_With_Infinity() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var child = new ChildControl(); + var window = new Window(); + var target = CreateTarget(window); + + target.Content = child; + target.Show(); + + Assert.Equal(Size.Infinity, child.MeasureSize); + } + } + + [Fact] + public void Child_Should_Be_Measured_With_Width_Height_When_Set() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var child = new ChildControl(); + var window = new Window(); + var target = CreateTarget(window); + + target.Width = 500; + target.Height = 600; + target.Content = child; + target.Show(); + + Assert.Equal(new Size(500, 600), child.MeasureSize); + } + } + + [Fact] + public void Should_Not_Have_Offset_On_Bounds_When_Content_Larger_Than_Max_Window_Size() + { + // Issue #3784. + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + var popupImpl = MockWindowingPlatform.CreatePopupMock(window.PlatformImpl); + + popupImpl.Setup(x => x.ClientSize).Returns(new Size(400, 480)); + + var child = new Canvas + { + Width = 400, + Height = 800, + }; + + var target = CreateTarget(window, popupImpl.Object); + target.Content = child; + + target.Show(); + + Assert.Equal(new Size(400, 480), target.Bounds.Size); + + // Issue #3784 causes this to be (0, 160) which makes no sense as Window has no + // parent control to be offset against. + Assert.Equal(new Point(0, 0), target.Bounds.Position); + } + } + + private PopupRoot CreateTarget(TopLevel popupParent, IPopupImpl impl = null) { - var result = new PopupRoot(popupParent, popupParent.PlatformImpl.CreatePopup()) + impl ??= popupParent.PlatformImpl.CreatePopup(); + + var result = new PopupRoot(popupParent, impl) { Template = new FuncControlTemplate((parent, scope) => new ContentPresenter @@ -217,5 +284,16 @@ namespace Avalonia.Controls.UnitTests.Primitives Popup = (Popup)this.GetVisualChildren().Single(); } } + + private class ChildControl : Control + { + public Size MeasureSize { get; private set; } + + protected override Size MeasureOverride(Size availableSize) + { + MeasureSize = availableSize; + return base.MeasureOverride(availableSize); + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index a4c0fa054b..1a2c93ac22 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -355,11 +355,11 @@ namespace Avalonia.Controls.UnitTests.Primitives { return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform: new MockWindowingPlatform(null, - () => + x => { if(UsePopupHost) return null; - return MockWindowingPlatform.CreatePopupMock().Object; + return MockWindowingPlatform.CreatePopupMock(x).Object; }))); } diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index fed63fc683..5382e6ea3e 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.UnitTests; @@ -355,6 +356,27 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Child_Should_Be_Measured_With_ClientSize_If_SizeToContent_Is_Manual_And_No_Width_Height_Specified() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var windowImpl = MockWindowingPlatform.CreateWindowMock(); + windowImpl.Setup(x => x.ClientSize).Returns(new Size(550, 450)); + + var child = new ChildControl(); + var target = new Window(windowImpl.Object) + { + SizeToContent = SizeToContent.Manual, + Content = child + }; + + target.Show(); + + Assert.Equal(new Size(550, 450), child.MeasureSize); + } + } + [Fact] public void Child_Should_Be_Measured_With_Infinity_If_SizeToContent_Is_WidthAndHeight() { @@ -375,6 +397,123 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Should_Not_Have_Offset_On_Bounds_When_Content_Larger_Than_Max_Window_Size() + { + // Issue #3784. + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var windowImpl = MockWindowingPlatform.CreateWindowMock(); + var clientSize = new Size(200, 200); + var maxClientSize = new Size(480, 480); + + windowImpl.Setup(x => x.Resize(It.IsAny())).Callback(size => + { + clientSize = size.Constrain(maxClientSize); + windowImpl.Object.Resized?.Invoke(clientSize); + }); + + windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); + + var child = new Canvas + { + Width = 400, + Height = 800, + }; + var target = new Window(windowImpl.Object) + { + SizeToContent = SizeToContent.WidthAndHeight, + Content = child + }; + + target.Show(); + + Assert.Equal(new Size(400, 480), target.Bounds.Size); + + // Issue #3784 causes this to be (0, 160) which makes no sense as Window has no + // parent control to be offset against. + Assert.Equal(new Point(0, 0), target.Bounds.Position); + } + } + + [Fact] + public void Width_Height_Should_Not_Be_NaN_After_Show_With_SizeToContent_WidthAndHeight() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var child = new Canvas + { + Width = 400, + Height = 800, + }; + + var target = new Window() + { + SizeToContent = SizeToContent.WidthAndHeight, + Content = child + }; + + target.Show(); + + Assert.Equal(400, target.Width); + Assert.Equal(800, target.Height); + } + } + + [Fact] + public void SizeToContent_Should_Not_Be_Lost_On_Show() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var child = new Canvas + { + Width = 400, + Height = 800, + }; + + var target = new Window() + { + SizeToContent = SizeToContent.WidthAndHeight, + Content = child + }; + + target.Show(); + + Assert.Equal(SizeToContent.WidthAndHeight, target.SizeToContent); + } + } + + [Fact] + public void Width_Height_Should_Be_Updated_When_SizeToContent_Is_WidthAndHeight() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var child = new Canvas + { + Width = 400, + Height = 800, + }; + + var target = new Window() + { + SizeToContent = SizeToContent.WidthAndHeight, + Content = child + }; + + target.Show(); + + Assert.Equal(400, target.Width); + Assert.Equal(800, target.Height); + + child.Width = 410; + target.LayoutManager.ExecuteLayoutPass(); + + Assert.Equal(410, target.Width); + Assert.Equal(800, target.Height); + Assert.Equal(SizeToContent.WidthAndHeight, target.SizeToContent); + } + } + private IWindowImpl CreateImpl(Mock renderer) { return Mock.Of(x => diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index 69eff0b65d..dcc29a9716 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -1,25 +1,12 @@ -using System.Diagnostics; -using System.IO; using System.Linq; -using Moq; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Diagnostics; -using Avalonia.Input; -using Avalonia.Platform; -using Avalonia.Rendering; -using Avalonia.Shared.PlatformSupport; using Avalonia.Styling; -using Avalonia.Themes.Default; +using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -using Avalonia.Media; -using System; -using System.Collections.Generic; -using Avalonia.Controls.UnitTests; -using Avalonia.UnitTests; namespace Avalonia.Layout.UnitTests { @@ -28,10 +15,8 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Grandchild_Size_Changed() { - using (var context = AvaloniaLocator.EnterScope()) + using (UnitTestApplication.Start(TestServices.StyledWindow)) { - RegisterServices(); - Border border; TextBlock textBlock; @@ -55,7 +40,6 @@ namespace Avalonia.Layout.UnitTests }; window.Show(); - window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.Equal(new Size(400, 400), border.Bounds.Size); textBlock.Width = 200; @@ -68,10 +52,8 @@ namespace Avalonia.Layout.UnitTests [Fact] public void Test_ScrollViewer_With_TextBlock() { - using (var context = AvaloniaLocator.EnterScope()) + using (UnitTestApplication.Start(TestServices.StyledWindow)) { - RegisterServices(); - ScrollViewer scrollViewer; TextBlock textBlock; @@ -79,7 +61,6 @@ namespace Avalonia.Layout.UnitTests { Width = 800, Height = 600, - SizeToContent = SizeToContent.WidthAndHeight, Content = scrollViewer = new ScrollViewer { Width = 200, @@ -99,7 +80,6 @@ namespace Avalonia.Layout.UnitTests window.Resources["ScrollBarThickness"] = 10.0; window.Show(); - window.LayoutManager.ExecuteInitialLayoutPass(window); Assert.Equal(new Size(800, 600), window.Bounds.Size); Assert.Equal(new Size(200, 200), scrollViewer.Bounds.Size); @@ -131,87 +111,5 @@ namespace Avalonia.Layout.UnitTests { return v.Bounds.Position; } - - class FormattedTextMock : IFormattedTextImpl - { - public FormattedTextMock(string text) - { - Text = text; - } - - public Size Constraint { get; set; } - - public string Text { get; } - - public Rect Bounds => Rect.Empty; - - public void Dispose() - { - } - - public IEnumerable GetLines() => new FormattedTextLine[0]; - - public TextHitTestResult HitTestPoint(Point point) => new TextHitTestResult(); - - public Rect HitTestTextPosition(int index) => new Rect(); - - public IEnumerable HitTestTextRange(int index, int length) => new Rect[0]; - - public Size Measure() => Constraint; - } - - private void RegisterServices() - { - var globalStyles = new Mock(); - var globalStylesResources = globalStyles.As(); - var outObj = (object)10; - globalStylesResources.Setup(x => x.TryGetResource("FontSizeNormal", out outObj)).Returns(true); - - var renderInterface = new Mock(); - renderInterface.Setup(x => - x.CreateFormattedText( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>())) - .Returns(new FormattedTextMock("TEST")); - - var streamGeometry = new Mock(); - streamGeometry.Setup(x => - x.Open()) - .Returns(new Mock().Object); - - renderInterface.Setup(x => - x.CreateStreamGeometry()) - .Returns(streamGeometry.Object); - - var windowImpl = new Mock(); - - Size clientSize = default(Size); - - windowImpl.SetupGet(x => x.ClientSize).Returns(() => clientSize); - windowImpl.Setup(x => x.Resize(It.IsAny())).Callback(s => clientSize = s); - windowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1024, 1024)); - windowImpl.SetupGet(x => x.Scaling).Returns(1); - - AvaloniaLocator.CurrentMutable - .Bind().ToConstant(new CursorFactoryMock()) - .Bind().ToConstant(new AssetLoader()) - .Bind().ToConstant(new Mock().Object) - .Bind().ToConstant(globalStyles.Object) - .Bind().ToConstant(new AppBuilder().RuntimePlatform) - .Bind().ToConstant(renderInterface.Object) - .Bind().ToConstant(new Styler()) - .Bind().ToConstant(new MockFontManagerImpl()) - .Bind().ToConstant(new MockTextShaperImpl()) - .Bind().ToConstant(new Avalonia.Controls.UnitTests.WindowingPlatformMock(() => windowImpl.Object)); - - var theme = new DefaultTheme(); - globalStyles.Setup(x => x.IsStylesInitialized).Returns(true); - globalStyles.Setup(x => x.Styles).Returns(theme); - } } } diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 782e4a0974..b8b7512c9e 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -8,65 +8,118 @@ namespace Avalonia.UnitTests { public class MockWindowingPlatform : IWindowingPlatform { + private static readonly Size s_screenSize = new Size(1280, 1024); private readonly Func _windowImpl; - private readonly Func _popupImpl; + private readonly Func _popupImpl; - public MockWindowingPlatform(Func windowImpl = null, Func popupImpl = null ) + public MockWindowingPlatform( + Func windowImpl = null, + Func popupImpl = null ) { _windowImpl = windowImpl; _popupImpl = popupImpl; } - public static Mock CreateWindowMock(Func popupImpl = null) + public static Mock CreateWindowMock() { - var win = Mock.Of(x => x.Scaling == 1); - var mock = Mock.Get(win); - mock.Setup(x => x.Show()).Callback(() => + var windowImpl = new Mock(); + var position = new PixelPoint(); + var clientSize = new Size(800, 600); + + windowImpl.SetupAllProperties(); + windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); + windowImpl.Setup(x => x.Scaling).Returns(1); + windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object); + windowImpl.Setup(x => x.Position).Returns(() => position); + SetupToplevel(windowImpl); + + windowImpl.Setup(x => x.CreatePopup()).Returns(() => { - mock.Object.Activated?.Invoke(); + return CreatePopupMock(windowImpl.Object).Object; }); - mock.Setup(x => x.CreatePopup()).Returns(() => + + windowImpl.Setup(x => x.Dispose()).Callback(() => { - if (popupImpl != null) - return popupImpl(); - return CreatePopupMock().Object; + windowImpl.Object.Closed?.Invoke(); + }); + + windowImpl.Setup(x => x.Move(It.IsAny())).Callback(x => + { + position = x; + windowImpl.Object.PositionChanged?.Invoke(x); + }); + windowImpl.Setup(x => x.Resize(It.IsAny())).Callback(x => + { + clientSize = x.Constrain(s_screenSize); + windowImpl.Object.Resized?.Invoke(clientSize); }); - mock.Setup(x => x.Dispose()).Callback(() => + + windowImpl.Setup(x => x.Show()).Callback(() => { - mock.Object.Closed?.Invoke(); + windowImpl.Object.Activated?.Invoke(); }); - 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; + + return windowImpl; } - static void SetupToplevel(Mock mock) where T : class, ITopLevelImpl + public static Mock CreatePopupMock(IWindowBaseImpl parent) { - mock.SetupGet(x => x.MouseDevice).Returns(new MouseDevice()); + var popupImpl = new Mock(); + + var positionerHelper = new ManagedPopupPositionerPopupImplHelper(parent, (pos, size, scale) => + { + popupImpl.Object.PositionChanged?.Invoke(pos); + popupImpl.Object.Resized?.Invoke(size); + }); + + var positioner = new ManagedPopupPositioner(positionerHelper); + + popupImpl.Setup(x => x.Scaling).Returns(1); + popupImpl.Setup(x => x.PopupPositioner).Returns(positioner); + + SetupToplevel(popupImpl); + + return popupImpl; } - public static Mock CreatePopupMock() + public static Mock CreateScreenMock() { - 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; + var screenImpl = new Mock(); + var bounds = new PixelRect(0, 0, (int)s_screenSize.Width, (int)s_screenSize.Height); + var screen = new Screen(96, bounds, bounds, true); + screenImpl.Setup(x => x.AllScreens).Returns(new[] { screen }); + screenImpl.Setup(x => x.ScreenCount).Returns(1); + return screenImpl; } public IWindowImpl CreateWindow() { - return _windowImpl?.Invoke() ?? CreateWindowMock(_popupImpl).Object; + if (_windowImpl is object) + { + return _windowImpl(); + } + else + { + var mock = CreateWindowMock(); + + if (_popupImpl is object) + { + mock.Setup(x => x.CreatePopup()).Returns(() => _popupImpl(mock.Object)); + } + + return mock.Object; + } } public IEmbeddableWindowImpl CreateEmbeddableWindow() { throw new NotImplementedException(); } + + private static void SetupToplevel(Mock mock) where T : class, ITopLevelImpl + { + mock.SetupGet(x => x.MouseDevice).Returns(new MouseDevice()); + } } }