Beispiel #1
0
            protected IntPtr HandleNCHitTest(IntPtr lParam)
            {
                // Because we still have the System Border (which technically extends beyond the actual window
                // into where the Drop shadows are), we can use DefWindowProc here to handle resizing, except
                // on the top. We'll handle that below
                var originalRet = Win32Interop.DefWindowProc(Hwnd, (uint)WM.NCHITTEST, IntPtr.Zero, lParam);

                if (originalRet != new IntPtr(1))
                {
                    return(originalRet);
                }

                // At this point, we know that the cursor is inside the client area so it
                // has to be either the little border at the top of our custom title bar,
                // the drag bar or something else in the XAML island. But the XAML Island
                // handles WM_NCHITTEST on its own so actually it cannot be the XAML
                // Island. Then it must be the drag bar or the little border at the top
                // which the user can use to move or resize the window.

                var point = PointToClient(PointFromLParam(lParam));

                RECT rcWindow;

                Win32Interop.GetWindowRect(Hwnd, out rcWindow);

                // On the Top border, the resize handle overlaps with the Titlebar area, which matches
                // a typical Win32 window or modern app window
                var  resizeBorderHeight = GetResizeHandleHeight();
                bool isOnResizeBorder   = point.Y < resizeBorderHeight;

                // Make sure the caption buttons still get precedence
                // This is where things get tricky too. On Win11, we still want to suppor the snap
                // layout feature when hovering over the Maximize button. Unfortunately no API exists
                // yet to call that manually if using a custom titlebar. But, if we return HT_MAXBUTTON
                // here, the pointer events no longer enter the window
                // See https://github.com/dotnet/wpf/issues/4825 for more on this...
                // To hack our way into making this work, we'll return HT_MAXBUTTON here, but manually
                // manage the state and handle stuff through the WM_NCLBUTTON... events
                // This only applies on Windows 11, Windows 10 will work normally b/c no snap layout thing

                if (_owner !.HitTestCaptionButtons(point))
                {
                    if (_isWindows11)
                    {
                        var result = _owner.HitTestMaximizeButton(point);

                        if (result)
                        {
                            _fakingMaximizeButton = true;
                            return(new IntPtr(9));
                        }
                    }
                }
Beispiel #2
0
            protected override IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
            {
                switch ((WM)msg)
                {
                case WM.NCCALCSIZE:
                    // Follows logic from how to extend window frame + WindowsTerminal + Firefox
                    // Windows Terminal only handles WPARAM = TRUE & only adjusts the top of the
                    // rgrc[0] RECT & gets the correct result
                    // Firefox, on the other hand, handles BOTH times WM_NCCALCSIZE is called,
                    // and modifies the RECT.
                    // This particularly differs from the "built-in" method in Avalonia in that
                    // I retain the SystemBorder & ability resize the window in the transparent
                    // area over the drop shadows, meaning resize handles don't overlap the window

                    if (wParam != IntPtr.Zero &&
                        _owner?.Window.CanResize == true)
                    {
                        var ncParams = Marshal.PtrToStructure <NCCALCSIZE_PARAMS>(lParam);

                        var originalTop = ncParams.rgrc[0].top;

                        var ret = Win32Interop.DefWindowProc(hWnd, (uint)WM.NCCALCSIZE, wParam, lParam);
                        if (ret != IntPtr.Zero)
                        {
                            return(ret);
                        }

                        var newSize = ncParams.rgrc[0];

                        if (newSize.Width == ncParams.rgrc[1].Width &&
                            newSize.Height == ncParams.rgrc[1].Height)
                        {
                            Marshal.StructureToPtr(ncParams, lParam, true);
                            return(IntPtr.Zero);
                        }

                        newSize.top = originalTop;

                        if (WindowState == WindowState.Maximized ||
                            WindowState == WindowState.FullScreen)
                        {
                            //newSize.top += GetResizeHandleHeight();
                        }
                        else
                        {
                            if (_owner != null)
                            {
                                newSize.left   += 8;
                                newSize.right  -= 8;
                                newSize.bottom -= 8;
                            }
                        }

                        ncParams.rgrc[0] = newSize;

                        Marshal.StructureToPtr(ncParams, lParam, true);
                        return(IntPtr.Zero);
                    }
                    break;

                //case WM.NCHITTEST:
                //    return HandleNCHitTest(lParam);

                case WM.SIZE:
                    EnsureExtended();

                    if (_fakingMaximizeButton)
                    {
                        // Sometimes the effect can get stuck, so if we resize, clear it
                        _owner.FakeMaximizePressed(false);
                        _wasFakeMaximizeDown = false;
                    }
                    break;

                case WM.ACTIVATE:
                    EnsureExtended();
                    break;

                case WM.NCMOUSEMOVE:
                    if (_fakingMaximizeButton)
                    {
                        var point = PointToClient(PointFromLParam(lParam));
                        _owner.FakeMaximizeHover(_owner.HitTestMaximizeButton(point));
                        return(IntPtr.Zero);
                    }
                    break;

                case WM.NCLBUTTONDOWN:
                    if (_fakingMaximizeButton)
                    {
                        var point = PointToClient(PointFromLParam(lParam));
                        _owner.FakeMaximizePressed(_owner.HitTestMaximizeButton(point));
                        _wasFakeMaximizeDown = true;

                        // This is important. If we don't tell the System we've handled this, we'll get that
                        // classic Win32 button showing when we mouse press, and that's not good
                        return(IntPtr.Zero);
                    }
                    break;

                case WM.NCLBUTTONUP:
                    if (_fakingMaximizeButton && _wasFakeMaximizeDown)
                    {
                        var point = PointToClient(PointFromLParam(lParam));
                        _owner.FakeMaximizePressed(false);
                        _wasFakeMaximizeDown = false;
                        _owner.FakeMaximizeClick();
                        return(IntPtr.Zero);
                    }
                    break;
                }

                return(base.WndProc(hWnd, msg, wParam, lParam));
            }