5

I need to create a frameless Qt Windows app that supports resizing.

If I use

setWindowFlags(Qt::FramelessWindowHint);

then I can resize only from bottom-right corner (like with the size grip, I guess QMainWindow includes it somehow?).

Is there any easy way to make it resizable from all sides like a normal window? Maybe something platform-specific (Windows)?

Alex P.
  • 3,697
  • 9
  • 45
  • 110
  • 1
    you can handle [WM_NCHITTEST](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645618(v=vs.85).aspx) and return for example *HTLEFT* when X_cursor - X_window <= SM_CXBORDER . and so for for 4 sides – RbMm Apr 19 '17 at 20:44
  • 1
    See http://stackoverflow.com/a/37507341/4149835 – Vladimir Bershov Apr 19 '17 at 20:49

1 Answers1

10

Solved it using WM_NCHITTEST, based on code from https://bugreports.qt.io/browse/QTBUG-40578 (bug report not related to this).

.h

class MainWindow : public QMainWindow
{
    Q_OBJECT
......
protected:
    bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;
};

.cpp

bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
    MSG* msg = static_cast<MSG*>(message);

    if (msg->message == WM_NCHITTEST)
    {
        if (isMaximized())
        {
            return false;
        }

        *result = 0;
        const LONG borderWidth = 8;
        RECT winrect;
        GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);

        // must be short to correctly work with multiple monitors (negative coordinates)
        short x = msg->lParam & 0x0000FFFF;
        short y = (msg->lParam & 0xFFFF0000) >> 16;

        bool resizeWidth = minimumWidth() != maximumWidth();
        bool resizeHeight = minimumHeight() != maximumHeight();
        if (resizeWidth)
        {
            //left border
            if (x >= winrect.left && x < winrect.left + borderWidth)
            {
                *result = HTLEFT;
            }
            //right border
            if (x < winrect.right && x >= winrect.right - borderWidth)
            {
                *result = HTRIGHT;
            }
        }
        if (resizeHeight)
        {
            //bottom border
            if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
            {
                *result = HTBOTTOM;
            }
            //top border
            if (y >= winrect.top && y < winrect.top + borderWidth)
            {
                *result = HTTOP;
            }
        }
        if (resizeWidth && resizeHeight)
        {
            //bottom left corner
            if (x >= winrect.left && x < winrect.left + borderWidth &&
                y < winrect.bottom && y >= winrect.bottom - borderWidth)
            {
                *result = HTBOTTOMLEFT;
            }
            //bottom right corner
            if (x < winrect.right && x >= winrect.right - borderWidth &&
                y < winrect.bottom && y >= winrect.bottom - borderWidth)
            {
                *result = HTBOTTOMRIGHT;
            }
            //top left corner
            if (x >= winrect.left && x < winrect.left + borderWidth &&
                y >= winrect.top && y < winrect.top + borderWidth)
            {
                *result = HTTOPLEFT;
            }
            //top right corner
            if (x < winrect.right && x >= winrect.right - borderWidth &&
                y >= winrect.top && y < winrect.top + borderWidth)
            {
                *result = HTTOPRIGHT;
            }
        }

        if (*result != 0)
            return true;

        QWidget *action = QApplication::widgetAt(QCursor::pos());
        if (action == this){
            *result = HTCAPTION;
            return true;
        }
    }

    return false;
}

implementation from Qt: Resize borderless widget did not work well: sometimes the window is moved during resizing (even in the first version without dragging)

Community
  • 1
  • 1
Alex P.
  • 3,697
  • 9
  • 45
  • 110
  • I tried your answer but my window flicker a lot when being resized from the left or top border, do you also got this behavior? – Cesar Nov 29 '22 at 01:42
  • This works great under Qt5 for me, but seems no longer work if trying to build under Qt6 (simply change `long*` to `qintptr*` doesn't seems to be working). Any idea why? – Gary Wang Feb 18 '23 at 16:36
  • To answer my own comment. There is a related Qt bug in Qt 6 that will cause this solution no longer working if you use the `Qt::WindowMinMaxButtonsHint` window flag for the flameless window (for example, to ensure the Winkey+Up/Down shortcut works). Removing this flag will make it work again, see QTBUG-112356 for details. – Gary Wang Apr 16 '23 at 12:11