diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml index 37014255cc..cb6016b324 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml +++ b/samples/ControlCatalog/DecoratedWindow.xaml @@ -1,9 +1,8 @@ - + xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window"> @@ -20,7 +19,13 @@ - Hello world! + + Hello world! + + Decorated + + CanResize + diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 9e8ff20e44..60b56ea580 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1172,6 +1172,8 @@ namespace Avalonia.Win32.Interop public int right; public int bottom; + public int Width => right - left; + public int Height => bottom - top; public RECT(Rect rect) { left = (int)rect.X; @@ -1179,6 +1181,34 @@ namespace Avalonia.Win32.Interop right = (int)(rect.X + rect.Width); bottom = (int)(rect.Y + rect.Height); } + + public void Offset(POINT pt) + { + left += pt.X; + right += pt.X; + top += pt.Y; + bottom += pt.Y; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct WINDOWPOS + { + public IntPtr hwnd; + public IntPtr hwndInsertAfter; + public int x; + public int y; + public int cx; + public int cy; + public uint flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NCCALCSIZE_PARAMS + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public RECT[] rgrc; + public WINDOWPOS lppos; } public struct TRACKMOUSEEVENT diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 18f0696cd8..4f97f9a472 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -35,6 +35,7 @@ namespace Avalonia.Win32 private bool _decorated = true; private bool _resizable = true; private bool _topmost = false; + private bool _taskbarIcon = true; private double _scaling = 1; private WindowState _showWindowState; private WindowState _lastWindowState; @@ -94,17 +95,25 @@ namespace Avalonia.Win32 { get { - var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); - var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE); - var padding = new UnmanagedMethods.RECT(); - - if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle)) + if (_decorated) { - return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom); + var style = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); + var exStyle = UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE); + + var padding = new RECT(); + + if (UnmanagedMethods.AdjustWindowRectEx(ref padding, style, false, exStyle)) + { + return new Thickness(-padding.left, -padding.top, padding.right, padding.bottom); + } + else + { + throw new Win32Exception(); + } } else { - throw new Win32Exception(); + return new Thickness(); } } } @@ -147,8 +156,7 @@ namespace Avalonia.Win32 if (value != ClientSize) { value *= Scaling; - value += BorderThickness; - + UnmanagedMethods.SetWindowPos( _hwnd, IntPtr.Zero, @@ -265,63 +273,7 @@ namespace Avalonia.Win32 return; } - var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); - - style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW; - - if (!value) - { - style ^= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW; - } - - UnmanagedMethods.RECT windowRect; - - UnmanagedMethods.GetWindowRect(_hwnd, out windowRect); - - Rect newRect; - var oldThickness = BorderThickness; - - UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style); - - if (value) - { - var thickness = BorderThickness; - - newRect = new Rect( - windowRect.left - thickness.Left, - windowRect.top - thickness.Top, - (windowRect.right - windowRect.left) + (thickness.Left + thickness.Right), - (windowRect.bottom - windowRect.top) + (thickness.Top + thickness.Bottom)); - } - else - { - newRect = new Rect( - windowRect.left + oldThickness.Left, - windowRect.top + oldThickness.Top, - (windowRect.right - windowRect.left) - (oldThickness.Left + oldThickness.Right), - (windowRect.bottom - windowRect.top) - (oldThickness.Top + oldThickness.Bottom)); - } - - UnmanagedMethods.SetWindowPos(_hwnd, IntPtr.Zero, (int)newRect.X, (int)newRect.Y, (int)newRect.Width, - (int)newRect.Height, - UnmanagedMethods.SetWindowPosFlags.SWP_NOZORDER | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE); - - _decorated = value; - - if(_decorated) - { - if (_resizable) - { - // If we switch decorations back on we need to restore WS_SizeFrame. - _resizable = false; - CanResize(true); - } - else - { - _resizable = true; - CanResize(false); - } - } + UpdateWMStyles(()=> _decorated = value); } public void Invalidate(Rect rect) @@ -486,6 +438,13 @@ namespace Avalonia.Win32 return IntPtr.Zero; + case WindowsMessage.WM_NCCALCSIZE: + if (ToInt32(wParam) == 1 && !_decorated) + { + return IntPtr.Zero; + } + break; + case UnmanagedMethods.WindowsMessage.WM_CLOSE: bool? preventClosing = Closing?.Invoke(); if (preventClosing == true) @@ -646,6 +605,14 @@ namespace Avalonia.Win32 new Point(0, 0), GetMouseModifiers(wParam)); break; + case WindowsMessage.WM_NCPAINT: + case WindowsMessage.WM_NCACTIVATE: + if (!_decorated) + { + return IntPtr.Zero; + } + break; + case UnmanagedMethods.WindowsMessage.WM_PAINT: UnmanagedMethods.PAINTSTRUCT ps; if (UnmanagedMethods.BeginPaint(_hwnd, out ps) != IntPtr.Zero) @@ -688,7 +655,7 @@ namespace Avalonia.Win32 MINMAXINFO mmi = Marshal.PtrToStructure(lParam); - if (_minSize.Width > 0) + if (_minSize.Width > 0) mmi.ptMinTrackSize.X = (int)((_minSize.Width * Scaling) + BorderThickness.Left + BorderThickness.Right); if (_minSize.Height > 0) @@ -859,7 +826,7 @@ namespace Avalonia.Win32 { MONITORINFO monitorInfo = MONITORINFO.Create(); - if (GetMonitorInfo(monitor,ref monitorInfo)) + if (GetMonitorInfo(monitor, ref monitorInfo)) { RECT rcMonitorArea = monitorInfo.rcMonitor; @@ -888,13 +855,22 @@ namespace Avalonia.Win32 return (int)(ptr.ToInt64() & 0xffffffff); } + public void ShowTaskbarIcon(bool value) { + if (_taskbarIcon == value) + { + return; + } + + _taskbarIcon = value; + var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_EXSTYLE); style &= ~(UnmanagedMethods.WindowStyles.WS_VISIBLE); style |= UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW; + if (value) style |= UnmanagedMethods.WindowStyles.WS_EX_APPWINDOW; else @@ -910,26 +886,68 @@ namespace Avalonia.Win32 } } - public void CanResize(bool value) + private void UpdateWMStyles(Action change) { - if (value == _resizable) + var oldDecorated = _decorated; + + var oldThickness = BorderThickness; + + change(); + + var style = (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE); + + const WindowStyles controlledFlags = WindowStyles.WS_OVERLAPPEDWINDOW; + + style = style | controlledFlags ^ controlledFlags; + + style |= WindowStyles.WS_OVERLAPPEDWINDOW; + + if (!_decorated) { - return; + style ^= (WindowStyles.WS_CAPTION | WindowStyles.WS_SYSMENU); } - if (_decorated) + if (!_resizable) { - var style = (UnmanagedMethods.WindowStyles)UnmanagedMethods.GetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE); + style ^= (WindowStyles.WS_SIZEFRAME); + } - if (value) - style |= UnmanagedMethods.WindowStyles.WS_SIZEFRAME; - else - style &= ~(UnmanagedMethods.WindowStyles.WS_SIZEFRAME); + GetClientRect(_hwnd, out var oldClientRect); + var oldClientRectOrigin = new UnmanagedMethods.POINT(); + ClientToScreen(_hwnd, ref oldClientRectOrigin); + oldClientRect.Offset(oldClientRectOrigin); + + + SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style); + + UnmanagedMethods.GetWindowRect(_hwnd, out var windowRect); + bool frameUpdated = false; + if (oldDecorated != _decorated) + { + var newRect = oldClientRect; + if (_decorated) + AdjustWindowRectEx(ref newRect, (uint)style, false, + GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE)); + SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); + frameUpdated = true; + } - UnmanagedMethods.SetWindowLong(_hwnd, (int)UnmanagedMethods.WindowLongParam.GWL_STYLE, (uint)style); + if (!frameUpdated) + SetWindowPos(_hwnd, IntPtr.Zero, 0, 0, 0, 0, + SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOZORDER | + SetWindowPosFlags.SWP_NOACTIVATE + | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + } + + public void CanResize(bool value) + { + if (value == _resizable) + { + return; } - _resizable = value; + UpdateWMStyles(()=> _resizable = value); } public void SetTopmost(bool value)