2

I'm coding a customized popup window with C++ using Win32. The condition for this popup window is that it can be only resized from the bottom down. The following is the implementation of such restriction:

RECT rcInitialWindowRectangle = {0};

//The dialog has WS_THICKFRAME style

LRESULT CALLBACK DlgWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    case WM_INITDIALOG:
        {
            //Set minimum window size
            ::GetWindowRect(hDlg, &rcInitialWindowRectangle);
        }
        break;

    case WM_SIZING:
        {
            //Restrict sizing on all sides but bottom
            if(wParam != WMSZ_BOTTOM)
            {
                RECT* pRcWnd = (RECT*)lParam;

                //Preserve all sides but bottom
                int b = pRcWnd->bottom;
                *pRcWnd = rcInitialWindowRectangle;
                pRcWnd->bottom = b;

                return TRUE;
            }
        }
        break;

    case WM_GETMINMAXINFO:
        {
            //The following is needed to restrict minimum window size
            int w = rcInitialWindowRectangle.right - rcInitialWindowRectangle.left;
            if(w != 0)
            {
                MINMAXINFO* pMMI = (MINMAXINFO*)lParam;

                pMMI->ptMinTrackSize.x = w;
                pMMI->ptMinTrackSize.y = rcInitialWindowRectangle.bottom - rcInitialWindowRectangle.top;

                pMMI->ptMaxTrackSize.x = w;
            }
        }
        break;

    case WM_NCHITTEST:
        {
            //The following is needed to display correct cursor for resizing
            POINT pnt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};

            RECT rcWnd;
            ::GetWindowRect(hDlg, &rcWnd);

            //L, T, R, B
            RECT rcBtm = {rcInitialWindowRectangle.left, 
                rcWnd.bottom - 16,  //Some arbitrary border 
                rcInitialWindowRectangle.right,
                rcWnd.bottom};

            return ::PtInRect(&rcBtm, pnt) ? HTNOWHERE : HTBORDER;
        }
        break;


    return 0;
}

So this works except one thing. On Windows 10, there's evidently a new feature -- when someone double-clicks on the bottom (or top) edge of a window -- here's an example with Notepad so that you can try:

enter image description here

that window is resized (stretched) to the top and bottom of the screen (akin to maximization, but only vertically.)

So my question is how do I block this double-click resizing? (In my case the top of the popup window should not move.)

PS. My first instinct was to block all double-clicks on the window's edge, but then I thought that maybe there's a less barbaric way to achieve this?

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • Maybe you made a window with `WS_SIZEBOX` style? If you have custom requirements on sizing behavior it might be a good idea to manage resizing manually. – user7860670 Jan 30 '18 at 07:51
  • For some reason the AeroSnap stuff in Windows was kludged in at a lower level than normal window resizing and there's no public API for any of it. – Jonathan Potter Jan 30 '18 at 11:02
  • Also FWIW, the feature you are trying to defeat is not new in Windows 10, it works in 8.1 as well. – Ben Voigt Jan 30 '18 at 21:11
  • @BenVoigt wow, I must really be distracted today. I'll delete my comments. – Mark Ransom Jan 30 '18 at 21:16
  • @BenVoigt: Nope. Doesn't work in Win8.1. I'm using that OS on my desktop right now. This used to work only as a double click on a title bar, which would maximize it. – c00000fd Jan 30 '18 at 21:43
  • 1
    @c00000fd: Double-clicking the bottom border of a window certainly does work on my Windows 8.1 installation. There's something affecting it other than simply Windows version. – Ben Voigt Jan 30 '18 at 22:40
  • @BenVoigt: Hah, you're right. I just fired up a VM with 8.1 and sure enough it works there. So you're correct. Interesting. I don't know why it doesn't seem to work on my main Win 8.1 box. I don't have any shell extensions of any customizations there. So go figure... Maybe it's because I haven't rebooted it in a few months and something got goofed up... – c00000fd Jan 30 '18 at 23:11
  • [Related](https://stackoverflow.com/questions/9321549/handling-aerosnap-message-in-wndproc). AeroSnap was disabled on this rig as well, [this](https://superuser.com/a/860636/369805) helped to switch it back on. – Laurie Stearn Jan 01 '21 at 12:11

2 Answers2

1

You are already handling WM_NCHITTEST. Handle WM_NCLBUTTONDBLCLK and don't forward to DefWindowProc unless the hit test (in wParam) indicates the lower frame.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I tried. I even tried this, which does nothing to this resizing: `case WM_NCLBUTTONDBLCLK: {::SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 0); return 1; }`. I'm guessing that this notification is sent after the fact. The only somewhat tedious "hack" that works is to manually override the window's size in `WM_WINDOWPOSCHANGING` – c00000fd Jan 30 '18 at 07:14
  • @c00000fd: Well you could also try handling `WM_NCLBUTTONDOWN`... but I think your problem may be that you are using a dialog procedure and these messages are sent to the window procedure. For dialog boxes, the system provides the window procedure --- but you can still subclass and override the handling. – Ben Voigt Jan 30 '18 at 07:18
  • @BarmakShemirani: That's a value of zero (FALSE). Return type from this message is just a true/false, not a hit detection zone. My guess is that you did this in a proper window procedure and not a dialog proc? – Ben Voigt Jan 30 '18 at 18:12
  • @BenVoigt yes, that was for a window. In dialog it should return `TRUE` That works as expected as well (tested in Windows 10) – Barmak Shemirani Jan 30 '18 at 18:47
  • @BarmakShemirani: I tried it as well and it didn't. Mind to show your actual code? – c00000fd Jan 30 '18 at 18:54
  • You have `LRESULT` in what seems to be a dialog procedure, I am confused. Dialog procedure should be `INT_PTR CALLBACK DlgWndProc(...)` I tested `case WM_NCLBUTTONDBLCLK: return TRUE;` in a dialog procedure and it works. – Barmak Shemirani Jan 30 '18 at 19:14
  • @BarmakShemirani: Those are just remnants of my experimentations. So yes, it's a dialog procedure. – c00000fd Jan 30 '18 at 19:51
0

here's the workaround that seems to work for me. It is not really about blocking the double-clicks. (Apart from subclassing the WndProc of a dialog box, which I haven't tried, I failed to block that double-click effect in DlgProc alone.) My workaround presented below is to achor the top of the popup window and let it drop down to the bottom of the screen, if the user who double-clicks the bottom border wants similar stuff to happen.

Since this does not answer my original question, I won't mark it as such.

//Add this case statement to my original code

case WM_WINDOWPOSCHANGING:
    {
        WINDOWPOS* pWP = (WINDOWPOS*)lParam;
        if(!(pWP->flags & (SWP_NOMOVE | SWP_NOSIZE)))
        {
            int w = rcInitialWindowRectangle.right - rcInitialWindowRectangle.left;
            if(w > 0)
            {
                //Anchor the top of the popup window
                pWP->x = rcInitialWindowRectangle.left;
                pWP->y = rcInitialWindowRectangle.top;

                pWP->cx = w;

                int h = pWP->cy;

                //Make sure that the height fits the screen
                POINT pnt = {pWP->x, pWP->y};
                MONITORINFO mi = {0};
                mi.cbSize = sizeof(mi);
                if(::GetMonitorInfo(::MonitorFromPoint(pnt, MONITOR_DEFAULTTONEAREST), &mi))
                {
                    if(pWP->y + h > mi.rcWork.bottom)
                    {
                        int nMinDefaultH = rcInitialWindowRectangle.bottom - 
                            rcInitialWindowRectangle.top;

                        int nAh = mi.rcWork.bottom - y;
                        if(nAh >= nMinDefaultH)
                            h = nAh;
                        else
                            h = nMinDefaultH;
                    }
                }

                pWP->cy = h;
            }
        }
    }
    break;
c00000fd
  • 20,994
  • 29
  • 177
  • 400