Browse Source

Merge pull request #3805 from AvaloniaUI/fixes/3791-maxautosizehint

Measure Window and PopupRoot to MaxAutoSizeHint.
pull/4138/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
f57e75dd45
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  2. 2
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  3. 15
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  4. 3
      src/Avalonia.Controls/TopLevel.cs
  5. 10
      src/Avalonia.Controls/Window.cs
  6. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  7. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  8. 5
      src/Avalonia.Layout/ILayoutRoot.cs
  9. 2
      src/Avalonia.Native/WindowImplBase.cs
  10. 2
      src/Avalonia.X11/X11Window.cs
  11. 26
      src/Windows/Avalonia.Win32/PopupImpl.cs
  12. 2
      src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs
  13. 14
      src/Windows/Avalonia.Win32/WindowImpl.cs
  14. 20
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
  15. 15
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  16. 2
      tests/Avalonia.UnitTests/MockWindowingPlatform.cs

5
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -1,5 +1,4 @@
using System; using System;
using Avalonia.Controls;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
@ -46,9 +45,9 @@ namespace Avalonia.Platform
IPlatformHandle Handle { get; } IPlatformHandle Handle { get; }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
Size MaxClientSize { get; } Size MaxAutoSizeHint { get; }
/// <summary> /// <summary>
/// Sets whether this window appears on top of all other windows /// Sets whether this window appears on top of all other windows

2
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -113,7 +113,7 @@ namespace Avalonia.Controls.Presenters
{ {
var scrollable = (ILogicalScrollable)Owner; var scrollable = (ILogicalScrollable)Owner;
var visualRoot = Owner.GetVisualRoot(); var visualRoot = Owner.GetVisualRoot();
var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxClientSize var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxAutoSizeHint
?? (visualRoot as TopLevel)?.ClientSize; ?? (visualRoot as TopLevel)?.ClientSize;
// If infinity is passed as the available size and we're virtualized then we need to // If infinity is passed as the available size and we're virtualized then we need to

15
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -121,7 +121,20 @@ namespace Avalonia.Controls.Primitives
protected override Size MeasureOverride(Size availableSize) 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 width = measured.Width;
var height = measured.Height; var height = measured.Height;
var widthCache = Width; var widthCache = Width;

3
src/Avalonia.Controls/TopLevel.cs

@ -276,9 +276,6 @@ namespace Avalonia.Controls
set { SetValue(AccessText.ShowAccessKeyProperty, value); } set { SetValue(AccessText.ShowAccessKeyProperty, value); }
} }
/// <inheritdoc/>
Size ILayoutRoot.MaxClientSize => Size.Infinity;
/// <inheritdoc/> /// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;

10
src/Avalonia.Controls/Window.cs

@ -188,7 +188,7 @@ namespace Avalonia.Controls
impl.Closing = HandleClosing; impl.Closing = HandleClosing;
impl.GotInputWhenDisabled = OnGotInputWhenDisabled; impl.GotInputWhenDisabled = OnGotInputWhenDisabled;
impl.WindowStateChanged = HandleWindowStateChanged; impl.WindowStateChanged = HandleWindowStateChanged;
_maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); _maxPlatformClientSize = PlatformImpl?.MaxAutoSizeHint ?? default(Size);
this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x));
PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar); PlatformImpl?.ShowTaskbarIcon(ShowInTaskbar);
@ -318,9 +318,6 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => PlatformImpl?.BeginResizeDrag(edge, e); public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e) => PlatformImpl?.BeginResizeDrag(edge, e);
/// <inheritdoc/>
Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize;
/// <inheritdoc/> /// <inheritdoc/>
Type IStyleable.StyleKey => typeof(Window); Type IStyleable.StyleKey => typeof(Window);
@ -663,15 +660,16 @@ namespace Avalonia.Controls
var sizeToContent = SizeToContent; var sizeToContent = SizeToContent;
var clientSize = ClientSize; var clientSize = ClientSize;
var constraint = clientSize; var constraint = clientSize;
var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity;
if (sizeToContent.HasFlagCustom(SizeToContent.Width)) if (sizeToContent.HasFlagCustom(SizeToContent.Width))
{ {
constraint = constraint.WithWidth(double.PositiveInfinity); constraint = constraint.WithWidth(maxAutoSize.Width);
} }
if (sizeToContent.HasFlagCustom(SizeToContent.Height)) if (sizeToContent.HasFlagCustom(SizeToContent.Height))
{ {
constraint = constraint.WithHeight(double.PositiveInfinity); constraint = constraint.WithHeight(maxAutoSize.Height);
} }
var result = base.MeasureOverride(constraint); var result = base.MeasureOverride(constraint);

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -44,7 +44,7 @@ namespace Avalonia.DesignerSupport.Remote
public IPlatformHandle Handle { get; } public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; } public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; } public Action<WindowState> WindowStateChanged { get; set; }
public Size MaxClientSize { get; } = new Size(4096, 4096); public Size MaxAutoSizeHint { get; } = new Size(4096, 4096);
public event Action LostFocus public event Action LostFocus
{ {
add {} add {}

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -19,7 +19,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public Action Activated { get; set; } public Action Activated { get; set; }
public IPlatformHandle Handle { get; } public IPlatformHandle Handle { get; }
public Size MaxClientSize { get; } public Size MaxAutoSizeHint { get; }
public Size ClientSize { get; } public Size ClientSize { get; }
public double Scaling { get; } = 1.0; public double Scaling { get; } = 1.0;
public IEnumerable<object> Surfaces { get; } public IEnumerable<object> Surfaces { get; }

5
src/Avalonia.Layout/ILayoutRoot.cs

@ -10,11 +10,6 @@ namespace Avalonia.Layout
/// </summary> /// </summary>
Size ClientSize { get; } Size ClientSize { get; }
/// <summary>
/// The maximum client size available.
/// </summary>
Size MaxClientSize { get; }
/// <summary> /// <summary>
/// The scaling factor to use in layout. /// The scaling factor to use in layout.
/// </summary> /// </summary>

2
src/Avalonia.Native/WindowImplBase.cs

@ -336,7 +336,7 @@ namespace Avalonia.Native
_native.BeginMoveDrag(); _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(); .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
public void SetTopmost(bool value) public void SetTopmost(bool value)

2
src/Avalonia.X11/X11Window.cs

@ -922,7 +922,7 @@ namespace Avalonia.X11
public IScreenImpl Screen => _platform.Screens; 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(); .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();

26
src/Windows/Avalonia.Win32/PopupImpl.cs

@ -8,12 +8,35 @@ namespace Avalonia.Win32
class PopupImpl : WindowImpl, IPopupImpl class PopupImpl : WindowImpl, IPopupImpl
{ {
private bool _dropShadowHint = true; private bool _dropShadowHint = true;
private Size? _maxAutoSize;
public override void Show() public override void Show()
{ {
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); 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) protected override IntPtr CreateWindowOverride(ushort atom)
{ {
UnmanagedMethods.WindowStyles style = UnmanagedMethods.WindowStyles style =
@ -47,6 +70,9 @@ namespace Avalonia.Win32
{ {
switch ((UnmanagedMethods.WindowsMessage)msg) switch ((UnmanagedMethods.WindowsMessage)msg)
{ {
case UnmanagedMethods.WindowsMessage.WM_DISPLAYCHANGE:
_maxAutoSize = null;
goto default;
case UnmanagedMethods.WindowsMessage.WM_MOUSEACTIVATE: case UnmanagedMethods.WindowsMessage.WM_MOUSEACTIVATE:
return (IntPtr)UnmanagedMethods.MouseActivate.MA_NOACTIVATE; return (IntPtr)UnmanagedMethods.MouseActivate.MA_NOACTIVATE;
default: default:

2
src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs

@ -393,6 +393,8 @@ namespace Avalonia.Win32
case WindowsMessage.WM_GETMINMAXINFO: case WindowsMessage.WM_GETMINMAXINFO:
{ {
MINMAXINFO mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam); MINMAXINFO mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam);
_maxTrackSize = mmi.ptMaxTrackSize;
if (_minSize.Width > 0) if (_minSize.Width > 0)
{ {

14
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -66,6 +66,7 @@ namespace Avalonia.Win32
private OleDropTarget _dropTarget; private OleDropTarget _dropTarget;
private Size _minSize; private Size _minSize;
private Size _maxSize; private Size _maxSize;
private POINT _maxTrackSize;
private WindowImpl _parent; private WindowImpl _parent;
public WindowImpl() public WindowImpl()
@ -168,16 +169,7 @@ namespace Avalonia.Win32
public IPlatformHandle Handle { get; private set; } public IPlatformHandle Handle { get; private set; }
public Size MaxClientSize public virtual Size MaxAutoSizeHint => new Size(_maxTrackSize.X / Scaling, _maxTrackSize.Y / Scaling);
{
get
{
return (new Size(
GetSystemMetrics(SystemMetric.SM_CXMAXTRACK),
GetSystemMetrics(SystemMetric.SM_CYMAXTRACK))
- BorderThickness) / Scaling;
}
}
public IMouseDevice MouseDevice => _mouseDevice; public IMouseDevice MouseDevice => _mouseDevice;
@ -211,6 +203,8 @@ namespace Avalonia.Win32
public WindowTransparencyLevel TransparencyLevel { get; private set; } public WindowTransparencyLevel TransparencyLevel { get; private set; }
protected IntPtr Hwnd => _hwnd;
public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel) public void SetTransparencyLevelHint (WindowTransparencyLevel transparencyLevel)
{ {
TransparencyLevel = EnableBlur(transparencyLevel); TransparencyLevel = EnableBlur(transparencyLevel);

20
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
@ -181,18 +182,21 @@ namespace Avalonia.Controls.UnitTests.Primitives
} }
[Fact] [Fact]
public void Child_Should_Be_Measured_With_Infinity() public void Child_Should_Be_Measured_With_MaxAutoSizeHint()
{ {
using (UnitTestApplication.Start(TestServices.StyledWindow)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var child = new ChildControl(); var child = new ChildControl();
var window = new Window(); 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.Content = child;
target.Show(); 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.Content = child;
target.Show(); 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.Content = child;
target.Show(); 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 private class ChildControl : Control
{ {
public Size MeasureSize { get; private set; } public List<Size> MeasureSizes { get; } = new List<Size>();
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
MeasureSize = availableSize; MeasureSizes.Add(availableSize);
return base.MeasureOverride(availableSize); return base.MeasureOverride(availableSize);
} }
} }

15
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -297,12 +297,12 @@ namespace Avalonia.Controls.UnitTests
{ {
var parentWindowImpl = MockWindowingPlatform.CreateWindowMock(); var parentWindowImpl = MockWindowingPlatform.CreateWindowMock();
parentWindowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480)); 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); parentWindowImpl.Setup(x => x.Scaling).Returns(1);
var windowImpl = MockWindowingPlatform.CreateWindowMock(); var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.ClientSize).Returns(new Size(320, 200)); 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); windowImpl.Setup(x => x.Scaling).Returns(1);
var parentWindowServices = TestServices.StyledWindow.With( var parentWindowServices = TestServices.StyledWindow.With(
@ -381,12 +381,15 @@ namespace Avalonia.Controls.UnitTests
} }
[Fact] [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)) using (UnitTestApplication.Start(TestServices.StyledWindow))
{ {
var windowImpl = MockWindowingPlatform.CreateWindowMock();
windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(new Size(1200, 1000));
var child = new ChildControl(); var child = new ChildControl();
var target = new Window var target = new Window(windowImpl.Object)
{ {
Width = 100, Width = 100,
Height = 50, Height = 50,
@ -394,10 +397,10 @@ namespace Avalonia.Controls.UnitTests
Content = child Content = child
}; };
Show(target); target.Show();
Assert.Equal(1, child.MeasureSizes.Count); Assert.Equal(1, child.MeasureSizes.Count);
Assert.Equal(Size.Infinity, child.MeasureSizes[0]); Assert.Equal(new Size(1200, 1000), child.MeasureSizes[0]);
} }
} }

2
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@ -28,6 +28,7 @@ namespace Avalonia.UnitTests
windowImpl.SetupAllProperties(); windowImpl.SetupAllProperties();
windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); 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.Scaling).Returns(1);
windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object); windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object);
windowImpl.Setup(x => x.Position).Returns(() => position); windowImpl.Setup(x => x.Position).Returns(() => position);
@ -79,6 +80,7 @@ namespace Avalonia.UnitTests
popupImpl.SetupAllProperties(); popupImpl.SetupAllProperties();
popupImpl.Setup(x => x.ClientSize).Returns(() => clientSize); 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.Scaling).Returns(1);
popupImpl.Setup(x => x.PopupPositioner).Returns(positioner); popupImpl.Setup(x => x.PopupPositioner).Returns(positioner);

Loading…
Cancel
Save