Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-134283

Windows Frameless windows broken

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P1: Critical
    • 6.9.0 Beta3
    • 6.8.1
    • Widgets: Main Window
    • None
    • windows10 and windows 11
    • Windows

    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.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            paddle Pierre B
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes