From 9feb2376a0b2e11ca3d1ffa4eeade4fa8034f161 Mon Sep 17 00:00:00 2001 From: Nathan Garside Date: Tue, 15 Jun 2021 20:37:48 +0100 Subject: [PATCH 01/13] Squash toplevel-totalsize --- native/Avalonia.Native/src/OSX/window.mm | 15 +++++++++ .../Platform/SkiaPlatform/TopLevelImpl.cs | 2 ++ src/Avalonia.Controls/ApiCompatBaseline.txt | 4 ++- .../Offscreen/OffscreenTopLevelImpl.cs | 10 ++++++ .../Platform/ITopLevelImpl.cs | 5 +++ src/Avalonia.Controls/TopLevel.cs | 19 +++++++++++- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 1 + src/Avalonia.Headless/HeadlessWindowImpl.cs | 1 + src/Avalonia.Native/WindowImplBase.cs | 14 +++++++++ src/Avalonia.Native/avn.idl | 1 + src/Avalonia.X11/X11Structs.cs | 10 ++++++ src/Avalonia.X11/X11Window.cs | 31 +++++++++++++++++++ .../FramebufferToplevelImpl.cs | 1 + .../Wpf/WpfTopLevelImpl.cs | 1 + .../Interop/UnmanagedMethods.cs | 23 ++++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 10 ++++++ src/iOS/Avalonia.iOS/AvaloniaView.cs | 1 + .../MockWindowingPlatform.cs | 1 + 18 files changed, 148 insertions(+), 2 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 870345e543..6bd09fe08b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -202,6 +202,21 @@ public: } } + virtual HRESULT GetTotalSize(AvnSize* ret) override + { + @autoreleasepool + { + if(ret == nullptr) + return E_POINTER; + + auto frame = [Window frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } + } + virtual HRESULT GetScaling (double* ret) override { @autoreleasepool diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 60b772a183..8b732a92da 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -55,6 +55,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform public virtual Size ClientSize => Size.ToSize(RenderScaling); + public Size TotalSize => ClientSize; + public IMouseDevice MouseDevice { get; } = new MouseDevice(); public Action Closed { get; set; } diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index a79b3b4d7b..166a005e4d 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -11,4 +11,6 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -Total Issues: 12 +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.TotalSize' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.TotalSize.get()' is present in the implementation but not in the contract. +Total Issues: 14 diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index ca0e9d48b8..cb0c5d94e5 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -35,6 +35,16 @@ namespace Avalonia.Controls.Embedding.Offscreen } } + public Size TotalSize + { + get { return _clientSize; } + set + { + _clientSize = value; + Resized?.Invoke(value); + } + } + public double RenderScaling { get { return _scaling; } diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 09f38042a1..546184f8a6 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -22,6 +22,11 @@ namespace Avalonia.Platform /// Size ClientSize { get; } + /// + /// Gets the total size of the toplevel, excluding shadows. + /// + Size TotalSize { get; } + /// /// Gets the scaling factor for the toplevel. This is used for rendering. /// diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 7a92836ddf..e2d9e7e697 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -42,6 +42,12 @@ namespace Avalonia.Controls public static readonly DirectProperty ClientSizeProperty = AvaloniaProperty.RegisterDirect(nameof(ClientSize), o => o.ClientSize); + /// + /// Defines the property. + /// + public static readonly DirectProperty TotalSizeProperty = + AvaloniaProperty.RegisterDirect(nameof(TotalSize), o => o.TotalSize); + /// /// Defines the property. /// @@ -74,6 +80,7 @@ namespace Avalonia.Controls private readonly IPlatformRenderInterface _renderInterface; private readonly IGlobalStyles _globalStyles; private Size _clientSize; + private Size _totalSize; private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager _layoutManager; private Border _transparencyFallbackBorder; @@ -84,6 +91,7 @@ namespace Avalonia.Controls static TopLevel() { AffectsMeasure(ClientSizeProperty); + AffectsMeasure(TotalSizeProperty); TransparencyLevelHintProperty.Changed.AddClassHandler( (tl, e) => @@ -194,9 +202,18 @@ namespace Avalonia.Controls public Size ClientSize { get { return _clientSize; } - protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); } + protected set + { + SetAndRaise(ClientSizeProperty, ref _clientSize, value); + SetAndRaise(TotalSizeProperty, ref _totalSize, PlatformImpl.TotalSize); + } } + /// + /// Gets or sets the total size of the window. + /// + public Size TotalSize => _totalSize; + /// /// Gets or sets the that the TopLevel should use when possible. /// diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index eedfc52d9d..91bc93d897 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -21,6 +21,7 @@ namespace Avalonia.DesignerSupport.Remote public IPlatformHandle Handle { get; } public Size MaxAutoSizeHint { get; } public Size ClientSize { get; } + public Size TotalSize => ClientSize; public double RenderScaling { get; } = 1.0; public double DesktopScaling => 1.0; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs index af522f3e36..74f8c95ade 100644 --- a/src/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs @@ -41,6 +41,7 @@ namespace Avalonia.Headless } public Size ClientSize { get; set; } + public Size TotalSize => ClientSize; public double RenderScaling { get; } = 1; public double DesktopScaling => RenderScaling; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index f716464d14..18efd1a519 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -104,6 +104,20 @@ namespace Avalonia.Native } } + public Size TotalSize + { + get + { + if (_native != null) + { + var s = _native.TotalSize; + return new Size(s.Width, s.Height); + } + + return default; + } + } + public IEnumerable Surfaces => new[] { (_gpu ? _glSurface : (object)null), this diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index adcbeb2d3a..aeb456c3ea 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -435,6 +435,7 @@ interface IAvnWindowBase : IUnknown HRESULT Close(); HRESULT Activate(); HRESULT GetClientSize(AvnSize*ret); + HRESULT GetTotalSize(AvnSize*ret); HRESULT GetScaling(double*ret); HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize); HRESULT Resize(double width, double height); diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs index b1006b43ee..604fbf2b24 100644 --- a/src/Avalonia.X11/X11Structs.cs +++ b/src/Avalonia.X11/X11Structs.cs @@ -1740,6 +1740,16 @@ namespace Avalonia.X11 { public short W; public short H; } + + [StructLayout (LayoutKind.Sequential)] + [Serializable] + internal struct XFrameExtents + { + public int Left; + public int Right; + public int Top; + public int Bottom; + } [StructLayout (LayoutKind.Sequential)] diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 5ac4c4c9d0..c0a38eee95 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -297,6 +297,30 @@ namespace Avalonia.X11 public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling); + public Size TotalSize + { + get + { + XGetWindowProperty(_x11.Display, _handle, _x11.Atoms._NET_FRAME_EXTENTS, IntPtr.Zero, + new IntPtr(4), false, (IntPtr)Atom.AnyPropertyType, out var _, + out var _, out var nitems, out var _, out var prop); + + if (nitems.ToInt64() != 4) + { + // Window hasn't been mapped by the WM yet, so can't get the extents. + return ClientSize; + } + + var data = (IntPtr*)prop.ToPointer(); + var extents = new Thickness(data[0].ToInt32(), data[2].ToInt32(), data[1].ToInt32(), data[3].ToInt32()); + XFree(prop); + + return new Size( + (_realSize.Width + extents.Left + extents.Right) / RenderScaling, + (_realSize.Height + extents.Top + extents.Bottom) / RenderScaling); + } + } + public double RenderScaling { get @@ -589,6 +613,13 @@ namespace Avalonia.X11 private void OnPropertyChange(IntPtr atom, bool hasValue) { + if (atom == _x11.Atoms._NET_FRAME_EXTENTS) + { + // Occurs once the window has been mapped, which is the earliest the extents + // can be retrieved, so invoke event to force update of TopLevel.TotalSize. + Resized.Invoke(ClientSize); + } + if (atom == _x11.Atoms._NET_WM_STATE) { WindowState state = WindowState.Normal; diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 4bbb58e53e..7f231aee17 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -62,6 +62,7 @@ namespace Avalonia.LinuxFramebuffer } public Size ClientSize => ScaledSize; + public Size TotalSize => ClientSize; public IMouseDevice MouseDevice => new MouseDevice(); public IPopupImpl CreatePopup() => null; diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index fe1d625efb..eedf99b33a 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -100,6 +100,7 @@ namespace Avalonia.Win32.Interop.Wpf } Size ITopLevelImpl.ClientSize => _finalSize; + Size ITopLevelImpl.TotalSize => _finalSize; IMouseDevice ITopLevelImpl.MouseDevice => _mouse; double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index c137926e4c..ad409810b8 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -745,6 +745,26 @@ namespace Avalonia.Win32.Interop WM_DISPATCH_WORK_ITEM = WM_USER, } + public enum DwmWindowAttribute : uint + { + DWMWA_NCRENDERING_ENABLED = 1, + DWMWA_NCRENDERING_POLICY, + DWMWA_TRANSITIONS_FORCEDISABLED, + DWMWA_ALLOW_NCPAINT, + DWMWA_CAPTION_BUTTON_BOUNDS, + DWMWA_NONCLIENT_RTL_LAYOUT, + DWMWA_FORCE_ICONIC_REPRESENTATION, + DWMWA_FLIP3D_POLICY, + DWMWA_EXTENDED_FRAME_BOUNDS, + DWMWA_HAS_ICONIC_BITMAP, + DWMWA_DISALLOW_PEEK, + DWMWA_EXCLUDED_FROM_PEEK, + DWMWA_CLOAK, + DWMWA_CLOAKED, + DWMWA_FREEZE_REPRESENTATION, + DWMWA_LAST + }; + public enum MapVirtualKeyMapTypes : uint { MAPVK_VK_TO_VSC = 0x00, @@ -1388,6 +1408,9 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins); + [DllImport("dwmapi.dll")] + public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); + [DllImport("dwmapi.dll")] public static extern int DwmIsCompositionEnabled(out bool enabled); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3a3342fd14..bb3f78715d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -213,6 +213,16 @@ namespace Avalonia.Win32 } } + public Size TotalSize + { + get + { + DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT))); + + return new Size(rect.Width, rect.Height) / RenderScaling; + } + } + public IScreenImpl Screen { get; } public IPlatformHandle Handle { get; private set; } diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index 36a70ea410..ecd2a46b12 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -91,6 +91,7 @@ namespace Avalonia.iOS } public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height); + public Size TotalSize => ClientSize; public double RenderScaling => _view.ContentScaleFactor; public IEnumerable Surfaces { get; set; } public Action Input { get; set; } diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 8a24a8366f..51c202f6bd 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -29,6 +29,7 @@ namespace Avalonia.UnitTests windowImpl.SetupAllProperties(); windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); + windowImpl.Setup(x => x.TotalSize).Returns(() => clientSize); windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize); windowImpl.Setup(x => x.DesktopScaling).Returns(1); windowImpl.Setup(x => x.RenderScaling).Returns(1); From d77d1fd5dab36976232bfd9a565a9c2d7cc956cf Mon Sep 17 00:00:00 2001 From: Nathan Garside Date: Tue, 15 Jun 2021 21:51:43 +0100 Subject: [PATCH 02/13] Rename to FrameSize --- native/Avalonia.Native/src/OSX/window.mm | 2 +- .../Platform/SkiaPlatform/TopLevelImpl.cs | 2 +- src/Avalonia.Controls/ApiCompatBaseline.txt | 4 +-- .../Offscreen/OffscreenTopLevelImpl.cs | 10 +------- .../Platform/ITopLevelImpl.cs | 2 +- src/Avalonia.Controls/TopLevel.cs | 25 +++++++++++-------- src/Avalonia.Controls/Window.cs | 1 + src/Avalonia.DesignerSupport/Remote/Stubs.cs | 2 +- src/Avalonia.Headless/HeadlessWindowImpl.cs | 2 +- src/Avalonia.Native/WindowImplBase.cs | 4 +-- src/Avalonia.Native/avn.idl | 2 +- src/Avalonia.X11/X11Window.cs | 4 +-- .../FramebufferToplevelImpl.cs | 2 +- .../Wpf/WpfTopLevelImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- src/iOS/Avalonia.iOS/AvaloniaView.cs | 2 +- .../MockWindowingPlatform.cs | 2 +- 17 files changed, 33 insertions(+), 37 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 6bd09fe08b..76eca330a9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -202,7 +202,7 @@ public: } } - virtual HRESULT GetTotalSize(AvnSize* ret) override + virtual HRESULT GetFrameSize(AvnSize* ret) override { @autoreleasepool { diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 8b732a92da..1e1e7066d4 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -55,7 +55,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public virtual Size ClientSize => Size.ToSize(RenderScaling); - public Size TotalSize => ClientSize; + public Size FrameSize => ClientSize; public IMouseDevice MouseDevice { get; } = new MouseDevice(); diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 166a005e4d..f81506252a 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -11,6 +11,6 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.TotalSize' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.TotalSize.get()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. Total Issues: 14 diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index cb0c5d94e5..9242264470 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -35,15 +35,7 @@ namespace Avalonia.Controls.Embedding.Offscreen } } - public Size TotalSize - { - get { return _clientSize; } - set - { - _clientSize = value; - Resized?.Invoke(value); - } - } + public Size FrameSize => _clientSize; public double RenderScaling { diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 546184f8a6..d9fb22422a 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -25,7 +25,7 @@ namespace Avalonia.Platform /// /// Gets the total size of the toplevel, excluding shadows. /// - Size TotalSize { get; } + Size FrameSize { get; } /// /// Gets the scaling factor for the toplevel. This is used for rendering. diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index e2d9e7e697..65bd2b8d2f 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -43,10 +43,10 @@ namespace Avalonia.Controls AvaloniaProperty.RegisterDirect(nameof(ClientSize), o => o.ClientSize); /// - /// Defines the property. + /// Defines the property. /// - public static readonly DirectProperty TotalSizeProperty = - AvaloniaProperty.RegisterDirect(nameof(TotalSize), o => o.TotalSize); + public static readonly DirectProperty FrameSizeProperty = + AvaloniaProperty.RegisterDirect(nameof(FrameSize), o => o.FrameSize); /// /// Defines the property. @@ -80,7 +80,7 @@ namespace Avalonia.Controls private readonly IPlatformRenderInterface _renderInterface; private readonly IGlobalStyles _globalStyles; private Size _clientSize; - private Size _totalSize; + private Size _frameSize; private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager _layoutManager; private Border _transparencyFallbackBorder; @@ -91,7 +91,6 @@ namespace Avalonia.Controls static TopLevel() { AffectsMeasure(ClientSizeProperty); - AffectsMeasure(TotalSizeProperty); TransparencyLevelHintProperty.Changed.AddClassHandler( (tl, e) => @@ -202,17 +201,13 @@ namespace Avalonia.Controls public Size ClientSize { get { return _clientSize; } - protected set - { - SetAndRaise(ClientSizeProperty, ref _clientSize, value); - SetAndRaise(TotalSizeProperty, ref _totalSize, PlatformImpl.TotalSize); - } + protected set { SetAndRaise(ClientSizeProperty, ref _clientSize, value); } } /// /// Gets or sets the total size of the window. /// - public Size TotalSize => _totalSize; + public Size FrameSize => _frameSize; /// /// Gets or sets the that the TopLevel should use when possible. @@ -462,6 +457,14 @@ namespace Avalonia.Controls /// The event args. protected virtual void OnClosed(EventArgs e) => Closed?.Invoke(this, e); + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (change.Property == ClientSizeProperty) + { + SetAndRaise(FrameSizeProperty, ref _frameSize, PlatformImpl.FrameSize); + } + } + /// /// Tries to get a service from an , logging a /// warning if not found. diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 700c3d9bad..cc8e27c3ec 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -950,6 +950,7 @@ namespace Avalonia.Controls protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { + base.OnPropertyChanged(change); if (change.Property == SystemDecorationsProperty) { var typedNewValue = change.NewValue.GetValueOrDefault(); diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 91bc93d897..aaaaedd2a6 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -21,7 +21,7 @@ namespace Avalonia.DesignerSupport.Remote public IPlatformHandle Handle { get; } public Size MaxAutoSizeHint { get; } public Size ClientSize { get; } - public Size TotalSize => ClientSize; + public Size FrameSize => ClientSize; public double RenderScaling { get; } = 1.0; public double DesktopScaling => 1.0; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs index 74f8c95ade..b1d53bc599 100644 --- a/src/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs @@ -41,7 +41,7 @@ namespace Avalonia.Headless } public Size ClientSize { get; set; } - public Size TotalSize => ClientSize; + public Size FrameSize => ClientSize; public double RenderScaling { get; } = 1; public double DesktopScaling => RenderScaling; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 18efd1a519..f37c72cab4 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -104,13 +104,13 @@ namespace Avalonia.Native } } - public Size TotalSize + public Size FrameSize { get { if (_native != null) { - var s = _native.TotalSize; + var s = _native.FrameSize; return new Size(s.Width, s.Height); } diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index aeb456c3ea..89e20463d8 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -435,7 +435,7 @@ interface IAvnWindowBase : IUnknown HRESULT Close(); HRESULT Activate(); HRESULT GetClientSize(AvnSize*ret); - HRESULT GetTotalSize(AvnSize*ret); + HRESULT GetFrameSize(AvnSize*ret); HRESULT GetScaling(double*ret); HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize); HRESULT Resize(double width, double height); diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index c0a38eee95..8493238b68 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -297,7 +297,7 @@ namespace Avalonia.X11 public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling); - public Size TotalSize + public Size FrameSize { get { @@ -616,7 +616,7 @@ namespace Avalonia.X11 if (atom == _x11.Atoms._NET_FRAME_EXTENTS) { // Occurs once the window has been mapped, which is the earliest the extents - // can be retrieved, so invoke event to force update of TopLevel.TotalSize. + // can be retrieved, so invoke event to force update of TopLevel.FrameSize. Resized.Invoke(ClientSize); } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 7f231aee17..19607ec47e 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -62,7 +62,7 @@ namespace Avalonia.LinuxFramebuffer } public Size ClientSize => ScaledSize; - public Size TotalSize => ClientSize; + public Size FrameSize => ClientSize; public IMouseDevice MouseDevice => new MouseDevice(); public IPopupImpl CreatePopup() => null; diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index eedf99b33a..f78328e36c 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -100,7 +100,7 @@ namespace Avalonia.Win32.Interop.Wpf } Size ITopLevelImpl.ClientSize => _finalSize; - Size ITopLevelImpl.TotalSize => _finalSize; + Size ITopLevelImpl.FrameSize => _finalSize; IMouseDevice ITopLevelImpl.MouseDevice => _mouse; double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index bb3f78715d..6e105d2152 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -213,7 +213,7 @@ namespace Avalonia.Win32 } } - public Size TotalSize + public Size FrameSize { get { diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index ecd2a46b12..f11cd1983b 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -91,7 +91,7 @@ namespace Avalonia.iOS } public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height); - public Size TotalSize => ClientSize; + public Size FrameSize => ClientSize; public double RenderScaling => _view.ContentScaleFactor; public IEnumerable Surfaces { get; set; } public Action Input { get; set; } diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 51c202f6bd..8db8f16784 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -29,7 +29,7 @@ namespace Avalonia.UnitTests windowImpl.SetupAllProperties(); windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); - windowImpl.Setup(x => x.TotalSize).Returns(() => clientSize); + windowImpl.Setup(x => x.FrameSize).Returns(() => clientSize); windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize); windowImpl.Setup(x => x.DesktopScaling).Returns(1); windowImpl.Setup(x => x.RenderScaling).Returns(1); From e65241992797ac596e782a56eb02ffd3ac8ada48 Mon Sep 17 00:00:00 2001 From: Nathan Garside Date: Tue, 15 Jun 2021 22:09:35 +0100 Subject: [PATCH 03/13] Fix regression --- src/Avalonia.Controls/TopLevel.cs | 16 +++++++--------- src/Avalonia.Controls/Window.cs | 1 - src/Avalonia.Controls/WindowBase.cs | 1 + 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 65bd2b8d2f..bbd551ace3 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -168,6 +168,7 @@ namespace Avalonia.Controls styler?.ApplyStyles(this); ClientSize = impl.ClientSize; + FrameSize = impl.FrameSize; this.GetObservable(PointerOverElementProperty) .Select( @@ -207,7 +208,11 @@ namespace Avalonia.Controls /// /// Gets or sets the total size of the window. /// - public Size FrameSize => _frameSize; + public Size FrameSize + { + get { return _frameSize; } + protected set { SetAndRaise(FrameSizeProperty, ref _frameSize, value); } + } /// /// Gets or sets the that the TopLevel should use when possible. @@ -378,6 +383,7 @@ namespace Avalonia.Controls protected virtual void HandleResized(Size clientSize) { ClientSize = clientSize; + FrameSize = PlatformImpl.FrameSize; Width = clientSize.Width; Height = clientSize.Height; LayoutManager.ExecuteLayoutPass(); @@ -457,14 +463,6 @@ namespace Avalonia.Controls /// The event args. protected virtual void OnClosed(EventArgs e) => Closed?.Invoke(this, e); - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - if (change.Property == ClientSizeProperty) - { - SetAndRaise(FrameSizeProperty, ref _frameSize, PlatformImpl.FrameSize); - } - } - /// /// Tries to get a service from an , logging a /// warning if not found. diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index cc8e27c3ec..700c3d9bad 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -950,7 +950,6 @@ namespace Avalonia.Controls protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - base.OnPropertyChanged(change); if (change.Property == SystemDecorationsProperty) { var typedNewValue = change.NewValue.GetValueOrDefault(); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index cdcb499e98..2b31cef8bd 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -222,6 +222,7 @@ namespace Avalonia.Controls protected override void HandleResized(Size clientSize) { ClientSize = clientSize; + FrameSize = PlatformImpl.FrameSize; LayoutManager.ExecuteLayoutPass(); Renderer?.Resized(clientSize); } From 3c911839005ab0b56561aae39a303353bf812397 Mon Sep 17 00:00:00 2001 From: Nathan Garside Date: Wed, 16 Jun 2021 13:39:23 +0100 Subject: [PATCH 04/13] Fix when dwm disabled --- src/Windows/Avalonia.Win32/WindowImpl.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 6e105d2152..645cb5fb99 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -217,8 +217,13 @@ namespace Avalonia.Win32 { get { - DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT))); + if (DwmIsCompositionEnabled(out var compositionEnabled) != 0 || !compositionEnabled) + { + GetWindowRect(_hwnd, out var rcWindow); + return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling; + } + DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT))); return new Size(rect.Width, rect.Height) / RenderScaling; } } From d5f1365de911adc52bebc1433f05b49df8b49e0c Mon Sep 17 00:00:00 2001 From: Nathan Garside Date: Wed, 16 Jun 2021 15:21:28 +0100 Subject: [PATCH 05/13] Revert unused addition --- src/Avalonia.X11/X11Structs.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Avalonia.X11/X11Structs.cs b/src/Avalonia.X11/X11Structs.cs index 604fbf2b24..b1006b43ee 100644 --- a/src/Avalonia.X11/X11Structs.cs +++ b/src/Avalonia.X11/X11Structs.cs @@ -1740,16 +1740,6 @@ namespace Avalonia.X11 { public short W; public short H; } - - [StructLayout (LayoutKind.Sequential)] - [Serializable] - internal struct XFrameExtents - { - public int Left; - public int Right; - public int Top; - public int Bottom; - } [StructLayout (LayoutKind.Sequential)] From bccbf0570c9891d8b46db6105e394dcf630c86a7 Mon Sep 17 00:00:00 2001 From: Nathan Garside Date: Thu, 17 Jun 2021 11:06:32 +0100 Subject: [PATCH 06/13] Make FrameSize nullable --- .../Platform/SkiaPlatform/TopLevelImpl.cs | 2 +- src/Avalonia.Controls/ApiCompatBaseline.txt | 4 ++-- .../Embedding/Offscreen/OffscreenTopLevelImpl.cs | 2 +- src/Avalonia.Controls/Platform/ITopLevelImpl.cs | 2 +- src/Avalonia.Controls/TopLevel.cs | 8 ++++---- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 2 +- src/Avalonia.Headless/HeadlessWindowImpl.cs | 2 +- src/Avalonia.Native/WindowImplBase.cs | 2 +- src/Avalonia.X11/X11Window.cs | 4 ++-- .../Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs | 2 +- src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- src/iOS/Avalonia.iOS/AvaloniaView.cs | 2 +- tests/Avalonia.UnitTests/MockWindowingPlatform.cs | 1 - 14 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 1e1e7066d4..a72742580c 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -55,7 +55,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public virtual Size ClientSize => Size.ToSize(RenderScaling); - public Size FrameSize => ClientSize; + public Size? FrameSize => null; public IMouseDevice MouseDevice { get; } = new MouseDevice(); diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index f81506252a..443b085aa4 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -11,6 +11,6 @@ EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. Total Issues: 14 diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index 9242264470..83470f161d 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -35,7 +35,7 @@ namespace Avalonia.Controls.Embedding.Offscreen } } - public Size FrameSize => _clientSize; + public Size? FrameSize => null; public double RenderScaling { diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index d9fb22422a..6e53233898 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -25,7 +25,7 @@ namespace Avalonia.Platform /// /// Gets the total size of the toplevel, excluding shadows. /// - Size FrameSize { get; } + Size? FrameSize { get; } /// /// Gets the scaling factor for the toplevel. This is used for rendering. diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index bbd551ace3..7028dca769 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -45,8 +45,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty FrameSizeProperty = - AvaloniaProperty.RegisterDirect(nameof(FrameSize), o => o.FrameSize); + public static readonly DirectProperty FrameSizeProperty = + AvaloniaProperty.RegisterDirect(nameof(FrameSize), o => o.FrameSize); /// /// Defines the property. @@ -80,7 +80,7 @@ namespace Avalonia.Controls private readonly IPlatformRenderInterface _renderInterface; private readonly IGlobalStyles _globalStyles; private Size _clientSize; - private Size _frameSize; + private Size? _frameSize; private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager _layoutManager; private Border _transparencyFallbackBorder; @@ -208,7 +208,7 @@ namespace Avalonia.Controls /// /// Gets or sets the total size of the window. /// - public Size FrameSize + public Size? FrameSize { get { return _frameSize; } protected set { SetAndRaise(FrameSizeProperty, ref _frameSize, value); } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index aaaaedd2a6..c8203686f9 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -21,7 +21,7 @@ namespace Avalonia.DesignerSupport.Remote public IPlatformHandle Handle { get; } public Size MaxAutoSizeHint { get; } public Size ClientSize { get; } - public Size FrameSize => ClientSize; + public Size? FrameSize => null; public double RenderScaling { get; } = 1.0; public double DesktopScaling => 1.0; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs index b1d53bc599..7f4b9face4 100644 --- a/src/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs @@ -41,7 +41,7 @@ namespace Avalonia.Headless } public Size ClientSize { get; set; } - public Size FrameSize => ClientSize; + public Size? FrameSize => null; public double RenderScaling { get; } = 1; public double DesktopScaling => RenderScaling; public IEnumerable Surfaces { get; } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index f37c72cab4..ced9cea3a8 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -104,7 +104,7 @@ namespace Avalonia.Native } } - public Size FrameSize + public Size? FrameSize { get { diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 8493238b68..3425da5deb 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -297,7 +297,7 @@ namespace Avalonia.X11 public Size ClientSize => new Size(_realSize.Width / RenderScaling, _realSize.Height / RenderScaling); - public Size FrameSize + public Size? FrameSize { get { @@ -308,7 +308,7 @@ namespace Avalonia.X11 if (nitems.ToInt64() != 4) { // Window hasn't been mapped by the WM yet, so can't get the extents. - return ClientSize; + return null; } var data = (IntPtr*)prop.ToPointer(); diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 19607ec47e..73b1b87ce9 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -62,7 +62,7 @@ namespace Avalonia.LinuxFramebuffer } public Size ClientSize => ScaledSize; - public Size FrameSize => ClientSize; + public Size? FrameSize => null; public IMouseDevice MouseDevice => new MouseDevice(); public IPopupImpl CreatePopup() => null; diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index f78328e36c..73e46b9e13 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -100,7 +100,7 @@ namespace Avalonia.Win32.Interop.Wpf } Size ITopLevelImpl.ClientSize => _finalSize; - Size ITopLevelImpl.FrameSize => _finalSize; + Size? ITopLevelImpl.FrameSize => null; IMouseDevice ITopLevelImpl.MouseDevice => _mouse; double ITopLevelImpl.RenderScaling => PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 645cb5fb99..646a6f5739 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -213,7 +213,7 @@ namespace Avalonia.Win32 } } - public Size FrameSize + public Size? FrameSize { get { diff --git a/src/iOS/Avalonia.iOS/AvaloniaView.cs b/src/iOS/Avalonia.iOS/AvaloniaView.cs index f11cd1983b..0371a7759a 100644 --- a/src/iOS/Avalonia.iOS/AvaloniaView.cs +++ b/src/iOS/Avalonia.iOS/AvaloniaView.cs @@ -91,7 +91,7 @@ namespace Avalonia.iOS } public Size ClientSize => new Size(_view.Bounds.Width, _view.Bounds.Height); - public Size FrameSize => ClientSize; + public Size? FrameSize => null; public double RenderScaling => _view.ContentScaleFactor; public IEnumerable Surfaces { get; set; } public Action Input { get; set; } diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 8db8f16784..8a24a8366f 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -29,7 +29,6 @@ namespace Avalonia.UnitTests windowImpl.SetupAllProperties(); windowImpl.Setup(x => x.ClientSize).Returns(() => clientSize); - windowImpl.Setup(x => x.FrameSize).Returns(() => clientSize); windowImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize); windowImpl.Setup(x => x.DesktopScaling).Returns(1); windowImpl.Setup(x => x.RenderScaling).Returns(1); From f94e4eb9e75d69f3b12b436f2d036331f4d4a0d9 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 29 Jun 2021 16:54:29 +0200 Subject: [PATCH 07/13] fixes(ComboBox): Closing window on Alt+F4 KeyDown when ComboBox is focused --- src/Avalonia.Controls/ComboBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index f8728af87f..dd2109e6cd 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -205,7 +205,7 @@ namespace Avalonia.Controls if (e.Handled) return; - if (e.Key == Key.F4 || + if ((e.Key == Key.F4 && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) == false) || ((e.Key == Key.Down || e.Key == Key.Up) && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt))) { IsDropDownOpen = !IsDropDownOpen; From 83c141172f17de098907b7b207f4300f35e8ba73 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 29 Jun 2021 16:55:09 +0200 Subject: [PATCH 08/13] feat(tests): Add test to check closing window on Alt+F4 KeyDown when ComboBox is focused --- .../ComboBoxTests.cs | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 8f9c7fdb0b..cb2fd11175 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -201,6 +201,65 @@ namespace Avalonia.Controls.UnitTests } - } + } + + [Fact] + public void Close_Window_On_Alt_F4_When_ComboBox_Is_Focus() + { + var inputManagerMock = new Moq.Mock(); + var services = TestServices.StyledWindow.With(inputManager: inputManagerMock.Object); + + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + + window.KeyDown += (s, e) => + { + if (e.Handled == false + && e.KeyModifiers.HasAllFlags(KeyModifiers.Alt) == true + && e.Key == Key.F4 ) + { + e.Handled = true; + window.Close(); + } + }; + + var count = 0; + + var target = new ComboBox + { + Items = new[] { new Canvas() }, + SelectedIndex = 0, + Template = GetTemplate(), + }; + + window.Content = target; + + + window.Closing += + (sender, e) => + { + count++; + }; + + window.Show(); + + target.Focus(); + + _helper.Down(target); + _helper.Up(target); + Assert.True(target.IsDropDownOpen); + + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + KeyModifiers = KeyModifiers.Alt, + Key = Key.F4 + }); + + + Assert.Equal(1, count); + } + } } } From 5bea0e4791e284829b53fae2935a26de4e86a6ea Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 2 Jul 2021 14:44:04 +0200 Subject: [PATCH 09/13] Add "Star" static to GridLength --- src/Avalonia.Controls/GridLength.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index b8418949d9..ee6a146d61 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -77,6 +77,12 @@ namespace Avalonia.Controls /// public static GridLength Auto => new GridLength(0, GridUnitType.Auto); + /// + /// Gets an instance of that indicates that a row or column should + /// fill its content. + /// + public static GridLength Star => new GridLength(1, GridUnitType.Star); + /// /// Gets the unit of the . /// From fa3f2c89327b3c31f9dc737fe15152fe184c365c Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 2 Jul 2021 15:49:19 +0200 Subject: [PATCH 10/13] Adding more ToString() overloads to support DevTools --- src/Avalonia.Controls/RowDefinitions.cs | 8 +++-- src/Avalonia.Visuals/Media/BoxShadow.cs | 41 ++++++++++++++++++++++++ src/Avalonia.Visuals/Media/BoxShadows.cs | 20 +++++++++++- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/RowDefinitions.cs b/src/Avalonia.Controls/RowDefinitions.cs index 02ab12b5af..a5ed6ae09d 100644 --- a/src/Avalonia.Controls/RowDefinitions.cs +++ b/src/Avalonia.Controls/RowDefinitions.cs @@ -1,5 +1,4 @@ using System.Linq; -using Avalonia.Collections; namespace Avalonia.Controls { @@ -25,6 +24,11 @@ namespace Avalonia.Controls AddRange(GridLength.ParseLengths(s).Select(x => new RowDefinition(x))); } + public override string ToString() + { + return string.Join(",", this.Select(x => x.Height)); + } + /// /// Parses a string representation of row definitions collection. /// @@ -32,4 +36,4 @@ namespace Avalonia.Controls /// The . public static RowDefinitions Parse(string s) => new RowDefinitions(s); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Media/BoxShadow.cs b/src/Avalonia.Visuals/Media/BoxShadow.cs index 69395fd3b8..7696c6c34b 100644 --- a/src/Avalonia.Visuals/Media/BoxShadow.cs +++ b/src/Avalonia.Visuals/Media/BoxShadow.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Text; using Avalonia.Animation.Animators; using Avalonia.Utilities; @@ -75,6 +76,46 @@ namespace Avalonia.Media return rv; } } + + public override string ToString() + { + var sb = new StringBuilder(); + + if (IsEmpty) + { + return "none"; + } + + if (IsInset) + { + sb.Append("inset"); + } + + if (OffsetX != 0.0) + { + sb.Append($" {OffsetX}"); + } + + if (OffsetY != 0.0) + { + sb.Append($" {OffsetY}"); + } + + if (Blur != 0.0) + { + sb.Append($" {Blur}"); + } + + if (Spread != 0.0) + { + sb.Append($" {Spread}"); + } + + sb.Append($" {Color.ToString()}"); + + return sb.ToString(); + } + public static unsafe BoxShadow Parse(string s) { if(s == null) diff --git a/src/Avalonia.Visuals/Media/BoxShadows.cs b/src/Avalonia.Visuals/Media/BoxShadows.cs index 9e4d6aacb0..6e1b0e7f7d 100644 --- a/src/Avalonia.Visuals/Media/BoxShadows.cs +++ b/src/Avalonia.Visuals/Media/BoxShadows.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel; +using System.Text; using Avalonia.Animation.Animators; namespace Avalonia.Media @@ -43,6 +43,24 @@ namespace Avalonia.Media } } + public override string ToString() + { + var sb = new StringBuilder(); + + if (Count == 0) + { + return "none"; + } + + foreach (var boxShadow in this) + { + sb.Append(boxShadow + " "); + } + + return sb.ToString(); + + } + [EditorBrowsable(EditorBrowsableState.Never)] public struct BoxShadowsEnumerator { From 40e6282375b3fc9ae56464b39eb2421df3c8a3b6 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 2 Jul 2021 15:51:47 +0200 Subject: [PATCH 11/13] Use interpolation string --- src/Avalonia.Visuals/Media/BoxShadows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Media/BoxShadows.cs b/src/Avalonia.Visuals/Media/BoxShadows.cs index 6e1b0e7f7d..2b1714aded 100644 --- a/src/Avalonia.Visuals/Media/BoxShadows.cs +++ b/src/Avalonia.Visuals/Media/BoxShadows.cs @@ -54,7 +54,7 @@ namespace Avalonia.Media foreach (var boxShadow in this) { - sb.Append(boxShadow + " "); + sb.Append($"{boxShadow} "); } return sb.ToString(); From cddae052e87ce0b06839cee7b8ed4c32d63fa058 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Fri, 2 Jul 2021 16:46:10 +0200 Subject: [PATCH 12/13] Don't box params, prevent creating temporary string --- src/Avalonia.Visuals/Media/BoxShadow.cs | 10 +++++----- src/Avalonia.Visuals/Media/BoxShadows.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Visuals/Media/BoxShadow.cs b/src/Avalonia.Visuals/Media/BoxShadow.cs index 7696c6c34b..50f75365b0 100644 --- a/src/Avalonia.Visuals/Media/BoxShadow.cs +++ b/src/Avalonia.Visuals/Media/BoxShadow.cs @@ -93,25 +93,25 @@ namespace Avalonia.Media if (OffsetX != 0.0) { - sb.Append($" {OffsetX}"); + sb.AppendFormat(" {0}", OffsetX.ToString()); } if (OffsetY != 0.0) { - sb.Append($" {OffsetY}"); + sb.AppendFormat(" {0}", OffsetY.ToString()); } if (Blur != 0.0) { - sb.Append($" {Blur}"); + sb.AppendFormat(" {0}", Blur.ToString()); } if (Spread != 0.0) { - sb.Append($" {Spread}"); + sb.AppendFormat(" {0}", Spread.ToString()); } - sb.Append($" {Color.ToString()}"); + sb.AppendFormat(" {0}", Color.ToString()); return sb.ToString(); } diff --git a/src/Avalonia.Visuals/Media/BoxShadows.cs b/src/Avalonia.Visuals/Media/BoxShadows.cs index 2b1714aded..810ac70b99 100644 --- a/src/Avalonia.Visuals/Media/BoxShadows.cs +++ b/src/Avalonia.Visuals/Media/BoxShadows.cs @@ -54,7 +54,7 @@ namespace Avalonia.Media foreach (var boxShadow in this) { - sb.Append($"{boxShadow} "); + sb.AppendFormat("{0} ", boxShadow.ToString()); } return sb.ToString(); From 17c94d3cf3b43d71aea697f9a17c264ac80da87b Mon Sep 17 00:00:00 2001 From: amwx Date: Sun, 4 Jul 2021 21:33:52 -0500 Subject: [PATCH 13/13] Add impl from upstream --- src/Avalonia.Layout/ElementManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Layout/ElementManager.cs index cb13deb15f..3f106708e6 100644 --- a/src/Avalonia.Layout/ElementManager.cs +++ b/src/Avalonia.Layout/ElementManager.cs @@ -325,7 +325,10 @@ namespace Avalonia.Layout break; case NotifyCollectionChangedAction.Move: - throw new NotImplementedException(); + int size = args.OldItems != null ? args.OldItems.Count : 1; + OnItemsRemoved(args.OldStartingIndex, size); + OnItemsAdded(args.NewStartingIndex, size); + break; } } }