diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 8c99dffc28..b190c4f2e7 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -1,5 +1,4 @@ using System; -using Avalonia.Controls; namespace Avalonia.Platform { @@ -46,9 +45,9 @@ namespace Avalonia.Platform IPlatformHandle Handle { get; } /// - /// Gets the maximum size of a window on the system. + /// Gets a maximum client size hint for an auto-sizing window, in device-independent pixels. /// - Size MaxClientSize { get; } + Size MaxAutoSizeHint { get; } /// /// Sets whether this window appears on top of all other windows diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 83ce63f240..3fac440c40 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -113,7 +113,7 @@ namespace Avalonia.Controls.Presenters { var scrollable = (ILogicalScrollable)Owner; var visualRoot = Owner.GetVisualRoot(); - var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxClientSize + var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxAutoSizeHint ?? (visualRoot as TopLevel)?.ClientSize; // If infinity is passed as the available size and we're virtualized then we need to diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 854b0cf435..da7352b77f 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -121,7 +121,20 @@ namespace Avalonia.Controls.Primitives protected override Size MeasureOverride(Size availableSize) { - var measured = base.MeasureOverride(availableSize); + var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; + var constraint = availableSize; + + if (double.IsInfinity(constraint.Width)) + { + constraint = constraint.WithWidth(maxAutoSize.Width); + } + + if (double.IsInfinity(constraint.Height)) + { + constraint = constraint.WithHeight(maxAutoSize.Height); + } + + var measured = base.MeasureOverride(constraint); var width = measured.Width; var height = measured.Height; var widthCache = Width; diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 8335e03487..e431076d2c 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -276,9 +276,6 @@ namespace Avalonia.Controls set { SetValue(AccessText.ShowAccessKeyProperty, value); } } - /// - Size ILayoutRoot.MaxClientSize => Size.Infinity; - /// double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 474d845905..ce7b619821 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -188,7 +188,7 @@ namespace Avalonia.Controls impl.Closing = HandleClosing; impl.GotInputWhenDisabled = OnGotInputWhenDisabled; impl.WindowStateChanged = HandleWindowStateChanged; - _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); + _maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size); this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar); @@ -318,9 +318,6 @@ namespace Avalonia.Controls /// public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => PlatformImpl?.BeginResizeDrag(edge, e); - /// - Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize; - /// Type IStyleable.StyleKey => typeof(Window); @@ -663,15 +660,16 @@ namespace Avalonia.Controls var sizeToContent = SizeToContent; var clientSize = ClientSize; var constraint = clientSize; + var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; if (sizeToContent.HasFlagCustom(SizeToContent.Width)) { - constraint = constraint.WithWidth(double.PositiveInfinity); + constraint = constraint.WithWidth(maxAutoSize.Width); } if (sizeToContent.HasFlagCustom(SizeToContent.Height)) { - constraint = constraint.WithHeight(double.PositiveInfinity); + constraint = constraint.WithHeight(maxAutoSize.Height); } var result = base.MeasureOverride(constraint); diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index 844489ef97..cd64af60e2 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -44,7 +44,7 @@ namespace Avalonia.DesignerSupport.Remote public IPlatformHandle Handle { get; } public WindowState WindowState { get; set; } public Action WindowStateChanged { get; set; } - public Size MaxClientSize { get; } = new Size(4096, 4096); + public Size MaxAutoSizeHint { get; } = new Size(4096, 4096); public event Action LostFocus { add {} diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 64b3af4ea2..b001bc1b76 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -19,7 +19,7 @@ namespace Avalonia.DesignerSupport.Remote public Action Deactivated { get; set; } public Action Activated { get; set; } public IPlatformHandle Handle { get; } - public Size MaxClientSize { get; } + public Size MaxAutoSizeHint { get; } public Size ClientSize { get; } public double Scaling { get; } = 1.0; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Layout/ILayoutRoot.cs b/src/Avalonia.Layout/ILayoutRoot.cs index 56aca75871..e2f16b338a 100644 --- a/src/Avalonia.Layout/ILayoutRoot.cs +++ b/src/Avalonia.Layout/ILayoutRoot.cs @@ -10,11 +10,6 @@ namespace Avalonia.Layout /// Size ClientSize { get; } - /// - /// The maximum client size available. - /// - Size MaxClientSize { get; } - /// /// The scaling factor to use in layout. /// diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index a7ca528b2b..9783454e0e 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -336,7 +336,7 @@ namespace Avalonia.Native _native.BeginMoveDrag(); } - public Size MaxClientSize => Screen.AllScreens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity)) + public Size MaxAutoSizeHint => Screen.AllScreens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity)) .OrderByDescending(x => x.Width + x.Height).FirstOrDefault(); public void SetTopmost(bool value) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 1d41fe4bdd..90064cb28d 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -922,7 +922,7 @@ namespace Avalonia.X11 public IScreenImpl Screen => _platform.Screens; - public Size MaxClientSize => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity)) + public Size MaxAutoSizeHint => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity)) .OrderByDescending(x => x.Width + x.Height).FirstOrDefault(); diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs index efcf1ea674..7f27a9e841 100644 --- a/src/Windows/Avalonia.Win32/PopupImpl.cs +++ b/src/Windows/Avalonia.Win32/PopupImpl.cs @@ -8,12 +8,35 @@ namespace Avalonia.Win32 class PopupImpl : WindowImpl, IPopupImpl { private bool _dropShadowHint = true; + private Size? _maxAutoSize; public override void Show() { UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); } + public override Size MaxAutoSizeHint + { + get + { + if (_maxAutoSize is null) + { + var monitor = UnmanagedMethods.MonitorFromWindow( + Hwnd, + UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST); + + if (monitor != IntPtr.Zero) + { + var info = UnmanagedMethods.MONITORINFO.Create(); + UnmanagedMethods.GetMonitorInfo(monitor, ref info); + _maxAutoSize = info.rcWork.ToPixelRect().ToRect(Scaling).Size; + } + } + + return _maxAutoSize ?? Size.Infinity; + } + } + protected override IntPtr CreateWindowOverride(ushort atom) { UnmanagedMethods.WindowStyles style = @@ -47,6 +70,9 @@ namespace Avalonia.Win32 { switch ((UnmanagedMethods.WindowsMessage)msg) { + case UnmanagedMethods.WindowsMessage.WM_DISPLAYCHANGE: + _maxAutoSize = null; + goto default; case UnmanagedMethods.WindowsMessage.WM_MOUSEACTIVATE: return (IntPtr)UnmanagedMethods.MouseActivate.MA_NOACTIVATE; default: diff --git a/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs index 138553b962..50e71aeebe 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs @@ -393,6 +393,8 @@ namespace Avalonia.Win32 case WindowsMessage.WM_GETMINMAXINFO: { MINMAXINFO mmi = Marshal.PtrToStructure(lParam); + + _maxTrackSize = mmi.ptMaxTrackSize; if (_minSize.Width > 0) { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 81340fcde7..6024dc01da 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -66,6 +66,7 @@ namespace Avalonia.Win32 private OleDropTarget _dropTarget; private Size _minSize; private Size _maxSize; + private POINT _maxTrackSize; private WindowImpl _parent; public WindowImpl() @@ -168,16 +169,7 @@ namespace Avalonia.Win32 public IPlatformHandle Handle { get; private set; } - public Size MaxClientSize - { - get - { - return (new Size( - GetSystemMetrics(SystemMetric.SM_CXMAXTRACK), - GetSystemMetrics(SystemMetric.SM_CYMAXTRACK)) - - BorderThickness) / Scaling; - } - } + public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / Scaling, _maxTrackSize.Y / Scaling); public IMouseDevice MouseDevice => _mouseDevice; @@ -211,6 +203,8 @@ namespace Avalonia.Win32 public WindowTransparencyLevel TransparencyLevel { get; private set; } + protected IntPtr Hwnd => _hwnd; + public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel) { TransparencyLevel = EnableBlur(transparencyLevel); diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index 08b9c75dbc..f27ff3928c 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; @@ -181,18 +182,21 @@ namespace Avalonia.Controls.UnitTests.Primitives } [Fact] - public void Child_Should_Be_Measured_With_Infinity() + public void Child_Should_Be_Measured_With_MaxAutoSizeHint() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var child = new ChildControl(); var window = new Window(); - var target = CreateTarget(window); + var popupImpl = MockWindowingPlatform.CreatePopupMock(window.PlatformImpl); + popupImpl.Setup(x => x.MaxAutoSizeHint).Returns(new Size(1200, 1000)); + var target = CreateTarget(window, popupImpl.Object); target.Content = child; target.Show(); - Assert.Equal(Size.Infinity, child.MeasureSize); + Assert.Equal(1, child.MeasureSizes.Count); + Assert.Equal(new Size(1200, 1000), child.MeasureSizes[0]); } } @@ -210,7 +214,8 @@ namespace Avalonia.Controls.UnitTests.Primitives target.Content = child; target.Show(); - Assert.Equal(new Size(500, 600), child.MeasureSize); + Assert.Equal(1, child.MeasureSizes.Count); + Assert.Equal(new Size(500, 600), child.MeasureSizes[0]); } } @@ -228,7 +233,8 @@ namespace Avalonia.Controls.UnitTests.Primitives target.Content = child; target.Show(); - Assert.Equal(new Size(500, 600), child.MeasureSize); + Assert.Equal(1, child.MeasureSizes.Count); + Assert.Equal(new Size(500, 600), child.MeasureSizes[0]); } } @@ -365,11 +371,11 @@ namespace Avalonia.Controls.UnitTests.Primitives private class ChildControl : Control { - public Size MeasureSize { get; private set; } + public List MeasureSizes { get; } = new List(); protected override Size MeasureOverride(Size availableSize) { - MeasureSize = availableSize; + MeasureSizes.Add(availableSize); return base.MeasureOverride(availableSize); } } diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index 80c8a34ffd..e2b0def00b 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -297,12 +297,12 @@ namespace Avalonia.Controls.UnitTests { 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.MaxAutoSizeHint).Returns(new Size(1920, 1080)); parentWindowImpl.Setup(x => x.Scaling).Returns(1); 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.MaxAutoSizeHint).Returns(new Size(1920, 1080)); windowImpl.Setup(x => x.Scaling).Returns(1); var parentWindowServices = TestServices.StyledWindow.With( @@ -381,12 +381,15 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Child_Should_Be_Measured_With_Infinity_If_SizeToContent_Is_WidthAndHeight() + public void Child_Should_Be_Measured_With_MaxAutoSizeHint_If_SizeToContent_Is_WidthAndHeight() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { + var windowImpl = MockWindowingPlatform.CreateWindowMock(); + windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(new Size(1200, 1000)); + var child = new ChildControl(); - var target = new Window + var target = new Window(windowImpl.Object) { Width = 100, Height = 50, @@ -394,10 +397,10 @@ namespace Avalonia.Controls.UnitTests Content = child }; - Show(target); + target.Show(); Assert.Equal(1, child.MeasureSizes.Count); - Assert.Equal(Size.Infinity, child.MeasureSizes[0]); + Assert.Equal(new Size(1200, 1000), child.MeasureSizes[0]); } } diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index b3e4b4edbc..ee45433089 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -28,6 +28,7 @@ namespace Avalonia.UnitTests windowImpl.SetupAllProperties(); windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); + windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize); windowImpl.Setup(x => x.Scaling).Returns(1); windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object); windowImpl.Setup(x => x.Position).Returns(() => position); @@ -79,6 +80,7 @@ namespace Avalonia.UnitTests popupImpl.SetupAllProperties(); popupImpl.Setup(x => x.ClientSize).Returns(() => clientSize); + popupImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize); popupImpl.Setup(x => x.Scaling).Returns(1); popupImpl.Setup(x => x.PopupPositioner).Returns(positioner);