Details
-
Bug
-
Resolution: Fixed
-
P1: Critical
-
6.8.1
-
None
-
windows10 and windows 11
Description
Qt does NOT support frameless windows / custom title bar. Which is very incredible in 2025 IMO. There's virtually no professional applications that don't have custom title bar. It's ridiculous that Qt does not support this out of the box.
But on top of that Qt6 actually breaks the possibility to implement it yourself!
The following code was working perfectly on Qt 5.15 to achieve a custom title bar with native Windows support :
- Aero snaps working
- Resizing by the edges working
- Click and drag the title bar to move the window OK
- Double click title bar to maximize OK
- Shadows/(round corners for win11) drawn correctly
FramelessWindow::FramelessWindow(QWidget * parent, Qt::WindowFlags f) : QMainWindow( parent, f ) { setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::Window); HWND hwnd = (HWND)this->winId(); DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); const MARGINS shadow = { 1, 1, 1, 1 }; DwmExtendFrameIntoClientArea(HWND(winId()), &shadow); auto* titleBar = new CustomTitleBar(this); setMenuWidget(titleBar); } bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) { MSG* msg = reinterpret_cast<MSG*>(message); switch (msg->message) { case WM_NCCALCSIZE: { if (msg->wParam == TRUE) { NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam); *result = WVR_REDRAW; return true; } } case WM_NCHITTEST: { auto titleBar = menuWidget(); if (!titleBar) { return QMainWindow::nativeEvent(eventType, message, result); } *result = 0; const LONG border_width = 5; RECT winrect; GetWindowRect(HWND(winId()), &winrect); long x = GET_X_LPARAM(msg->lParam); long y = GET_Y_LPARAM(msg->lParam); bool resizeWidth = minimumWidth() != maximumWidth(); bool resizeHeight = minimumHeight() != maximumHeight(); if (resizeWidth) { //left border if (x >= winrect.left && x < winrect.left + border_width) { *result = HTLEFT; } //right border if (x < winrect.right && x >= winrect.right - border_width) { *result = HTRIGHT; } } if (resizeHeight) { //bottom border if (y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOM; } //top border if (y >= winrect.top && y < winrect.top + border_width) { *result = HTTOP; } } if (resizeWidth && resizeHeight) { //bottom left corner if (x >= winrect.left && x < winrect.left + border_width && y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOMLEFT; } //bottom right corner if (x < winrect.right && x >= winrect.right - border_width && y < winrect.bottom && y >= winrect.bottom - border_width) { *result = HTBOTTOMRIGHT; } //top left corner if (x >= winrect.left && x < winrect.left + border_width && y >= winrect.top && y < winrect.top + border_width) { *result = HTTOPLEFT; } //top right corner if (x < winrect.right && x >= winrect.right - border_width && y >= winrect.top && y < winrect.top + border_width) { *result = HTTOPRIGHT; } } if (0 != *result) return true; //*result still equals 0, that means the cursor locate OUTSIDE the frame area //but it may locate in titlebar area //support highdpi double dpr = this->devicePixelRatioF(); QPoint pos = titleBar->mapFromGlobal(QPoint(x / dpr, y / dpr)); if (!titleBar->rect().contains(pos)) return false; QWidget* child = titleBar->childAt(pos); if (!child) { *result = HTCAPTION; return true; } return false; } //end case WM_NCHITTEST default: return QMainWindow::nativeEvent(eventType, message, result); } return QMainWindow::nativeEvent(eventType, message, result); }
Now I upgraded to Qt 6.8.1 and nothing works anymore.
Please advise why SetWindowLong and/or DwmExtendFrameIntoClientArea don't work anymore on Qt6. And how to make it work again.
And please just implement support for frameless window on Qt! At least for Windows, MacOS and Linux It should have been done 15 years ago already.