Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Frameless (Qt + WinAPI) maximized window size is bigger than the availableGeometry()

Frameless (Qt + WinAPI) maximized window size is bigger than the availableGeometry()

Scheduled Pinned Locked Moved Solved General and Desktop
qt6winapiwin32framelesswindow
2 Posts 1 Posters 622 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    bibasmall
    wrote on 30 Mar 2024, 08:42 last edited by bibasmall
    #1

    Hello, guys!

    I'm struggling to solve the following problem. My window top left corner has negative coordinates when maximized (both x and y are -9), and it's size is actually bigger than the available area.
    I'm using WinAPI to get some platform specific features like Aero snap, so it becomes complicated to understand why exactly this happens.
    My window has the following styles and attributes set:

    setFlags(Qt::Window | Qt::FramelessWindowHint);
    SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
    

    WS_CAPTION is same as WS_BORDER | WS_DLGFRAME, so if I remove WS_DLGFRAME, maximized size is ok, and I see that the native maximizing animation disappears — but I get the flickering white border when resize because of WS_SIZEBOX (same as WS_THICKFRAME). Without WS_SIZEBOX I loose aero snap.
    I thought maybe I can do something with maximixed size in nativeEvent, so I tried the following:

    bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
    {
        if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE)
        {
            NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
            if (params.rgrc[0].top != 0)
            {
                --params.rgrc[0].top;
            }
            *result = WVR_REDRAW;
            return true; //this removes the native border and title
        }
        else if (msg->message == WM_GETMINMAXINFO)
        {
            if (IsMaximized((HWND)winId()))
            {
                MINMAXINFO* mmi = (MINMAXINFO*)msg->lParam;
                auto g = screen()->availableGeometry();
                mmi->ptMaxTrackSize.x = g.width() * devicePixelRatio();
                mmi->ptMaxTrackSize.y = g.height() * devicePixelRatio();
                *result = 0;
                return true;
            }
        }
    
        return QQuickWindow::nativeEvent(eventType, message, result);
    }
    

    For some reason, MINMAXINFO overloading does nothing until I set mmi->ptMaxTrackSize.x = g.width() * devicePixelRatio() - 1;. In this case, the size is almost nice, except that there is a 1 px empty gap between the right border of the screen and the window. So there is no way I can obtain the correct size with changing MINMAXINFO, because the real correct size should be actually same as in the above code. The initial ptMaxTrackSize is pretty strange: 3462, 1462 in mmi vs 3440, 1390 in availableGeometry).

    I can do something like that, but I'd like to keep QWindow::Maximized state, which is reset when I call setGeometry.

    bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
    {
        if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
        {
            setGeometry(screen()->availableGeometry()); //the window is no longer maximized
            return true;
        }
    
        return QQuickWindow::eventFilter(watched, event);
    }
    

    Since I'm not able to use QWindow::Maximized in setGeometry approach, this approach equals to implementing my own maximize, normalize and so on, what is undesirable.

    I also tried to add margins in maximized state, but creating a layout causes ugly flickering when resize, since the instance of my window is created in QML. And I'm unable to use setLayout, because I inherited QQuickWindow.

    Am I missing something?

    1 Reply Last reply
    0
    • B Offline
      B Offline
      bibasmall
      wrote on 2 Apr 2024, 18:46 last edited by bibasmall 4 Feb 2024, 18:46
      #2

      I have found out that this problem is mainly related to WinAPI + framelessness, not to Qt. I didn't manage to find any working WinAPI solution, for example, I've tried this one: melak47's solution. So I've chosen Qt way. This approach is not as concise as I expected from the WinAPI approach, but it works.
      Here is a code snippet describing only the necessary parts.

      .hpp

      class FramelessWindow : public QQuickWindow
      {
          Q_OBJECT
          QML_ELEMENT
          Q_PROPERTY(bool isMaximized READ isMaximized NOTIFY isMaximizedChanged)
      
      signals:
          void isMaximizedChanged();
      public:
          FramelessWindow() noexcept;
          Q_INVOKABLE void showNormal() noexcept;
          Q_INVOKABLE void showMaximized() noexcept;
          bool isMaximized() const noexcept;
      
      private:
          bool eventFilter(QObject* watched, QEvent* event) override;
          bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
      
          QRect restoredGeometry_;
          bool isMaximized_;
      };
      

      .cpp

      FramelessWindow::FramelessWindow() noexcept :
          isMaximized_ { false }
      {
          setFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMaximizeButtonHint);
          installEventFilter(this);
          SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
      }
      
      void FramelessWindow::showNormal() noexcept
      {
          setGeometry(restoredGeometry_);
          isMaximized_ = false;
          emit isMaximizedChanged();
      }
      
      void FramelessWindow::showMaximized() noexcept
      {
          restoredGeometry_ = geometry();
          setGeometry(screen()->availableGeometry());
          isMaximized_ = true;
          emit isMaximizedChanged();
      }
      
      bool FramelessWindow::isMaximized() const noexcept
      {
          return isMaximized_;
      }
      
      bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
      {
          QPoint cursorPos = QCursor::pos();
          qreal dpr = devicePixelRatio();
          QRect draggingArea = geometry();
          draggingArea.setHeight(32 * dpr);
          draggingArea.setY(draggingArea.y() + dpr * ResizeBorderWidth);
      
          if (draggingArea.contains(cursorPos))
          {
              if (event->type() == QEvent::MouseButtonPress)
              {
                  if (isMaximized_)
                  {
                      restoredGeometry_.moveTo({ QCursor::pos().x() - restoredGeometry_.width() / 2, QCursor::pos().y() - 10 });
                      showNormal();
                  }
                  startSystemMove();
                  return true;
              }
              else if (isResizable_ && event->type() == QEvent::MouseButtonDblClick)
              {
                  if (draggingArea.contains(cursorPos))
                  {
                      if (isMaximized_)
                      {
                          showNormal();
                      }
                      else
                      {
                          showMaximized();
                      }
                      return true;
                  }
              }
              else if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
              {
                  setGeometry(screen()->availableGeometry());
                  isMaximized_ = true;
                  emit isMaximizedChanged();
                  return true;
              }
          }
      
          return QQuickWindow::eventFilter(watched, event);
      }
      
      bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
      {
          if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE)
          {
              NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
              if (params.rgrc[0].top != 0)
              {
                  --params.rgrc[0].top;
              }
              *result = 0;
              return true;
          }
      
          return QQuickWindow::nativeEvent(eventType, message, result);
      }
      
      1 Reply Last reply
      0
      • B bibasmall has marked this topic as solved on 2 Apr 2024, 18:47

      1/2

      30 Mar 2024, 08:42

      • Login

      • Login or register to search.
      1 out of 2
      • First post
        1/2
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved