diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index da52c3116e..ed6339c403 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1305,6 +1305,9 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern int DwmIsCompositionEnabled(out bool enabled); + [DllImport("dwmapi.dll")] + public static extern bool DwmDefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult); + [DllImport("dwmapi.dll")] public static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs index 138553b962..82374893f1 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs @@ -12,11 +12,164 @@ using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { + public partial class WindowImpl + { + private int LEFTEXTENDWIDTH = 4; + private int RIGHTEXTENDWIDTH = 4; + private int BOTTOMEXTENDWIDTH = 4; + private int TOPEXTENDWIDTH = 100; + + // Hit test the frame for resizing and moving. + HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam) + { + // Get the point coordinates for the hit test. + var ptMouse = PointFromLParam(lParam); + + // Get the window rectangle. + GetWindowRect(hWnd, out var rcWindow); + + // Get the frame rectangle, adjusted for the style without a caption. + RECT rcFrame = new RECT(); + AdjustWindowRectEx(ref rcFrame, (uint)(WindowStyles.WS_OVERLAPPEDWINDOW & ~WindowStyles.WS_CAPTION), false, 0); + + // Determine if the hit test is for resizing. Default middle (1,1). + ushort uRow = 1; + ushort uCol = 1; + bool fOnResizeBorder = false; + + // Determine if the point is at the top or bottom of the window. + if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + TOPEXTENDWIDTH) + { + fOnResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top)); + uRow = 0; + } + else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - BOTTOMEXTENDWIDTH) + { + uRow = 2; + } + + // Determine if the point is at the left or right of the window. + if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + LEFTEXTENDWIDTH) + { + uCol = 0; // left side + } + else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - RIGHTEXTENDWIDTH) + { + uCol = 2; // right side + } + + // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT) + HitTestValues[][] hitTests = new[] + { + new []{ HitTestValues.HTTOPLEFT, fOnResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION, HitTestValues.HTTOPRIGHT }, + new []{ HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT }, + new []{ HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT }, + }; + + System.Diagnostics.Debug.WriteLine(hitTests[uRow][uCol]); + + return hitTests[uRow][uCol]; + } + + protected virtual unsafe IntPtr CustomCaptionProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool callDwp) + { + IntPtr lRet = IntPtr.Zero; + + callDwp = !DwmDefWindowProc(hWnd, msg, wParam, lParam, ref lRet); + + MARGINS margins = new MARGINS { cxLeftWidth = 0, cxRightWidth = 0, cyBottomHeight = 0, cyTopHeight = 100 }; + RECT border_thickness = new RECT(); + + switch ((WindowsMessage)msg) + { + case WindowsMessage.WM_ACTIVATE: + { + if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME)) + { + AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0); + border_thickness.left *= -1; + border_thickness.top *= -1; + } + else if (GetStyle().HasFlag(WindowStyles.WS_BORDER)) + { + border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 }; + } + + LEFTEXTENDWIDTH = border_thickness.left; + RIGHTEXTENDWIDTH = border_thickness.right; + BOTTOMEXTENDWIDTH = border_thickness.bottom; + + // Extend the frame into the client area. + margins.cxLeftWidth = border_thickness.left; + margins.cxRightWidth = border_thickness.right; + margins.cyBottomHeight = border_thickness.bottom; + margins.cyTopHeight = border_thickness.top; + + var hr = DwmExtendFrameIntoClientArea(hWnd, ref margins); + + //if (hr < 0) + { + // Handle the error. + } + + lRet = IntPtr.Zero; + callDwp = true; + break; + } + + case WindowsMessage.WM_NCCALCSIZE: + { + if (ToInt32(wParam) == 1) + { + lRet = IntPtr.Zero; + callDwp = false; + } + break; + } + + case WindowsMessage.WM_NCHITTEST: + if (lRet == IntPtr.Zero) + { + lRet = (IntPtr)HitTestNCA(hWnd, wParam, lParam); + + if (((HitTestValues)lRet) != HitTestValues.HTNOWHERE) + { + callDwp = false; + } + } + break; + } + + return lRet; + } + } + + public partial class WindowImpl + { + protected virtual unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + { + IntPtr lRet = IntPtr.Zero; + bool callDwp = true; + + if (DwmIsCompositionEnabled(out bool enabled) == 0) + { + lRet = CustomCaptionProc(hWnd, msg, wParam, lParam, ref callDwp); + } + + if (callDwp) + { + lRet = AppWndProc(hWnd, msg, wParam, lParam); + } + + return lRet; + } + } + public partial class WindowImpl { [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")] - protected virtual unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + protected virtual unsafe IntPtr AppWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { const double wheelDelta = 120.0; uint timestamp = unchecked((uint)GetMessageTime());