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

Reports bad geometry between displays with custom frameless window (Win 10)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P3: Somewhat important
    • None
    • 5.13.1
    • Widgets: Main Window
    • None
    • Windows 10 with Aero Snap
    • Windows

    Description

      For one of my applications, I wanted to have a window customized under the Windows operating system (like Firefox, Avast, Microsoft Word, etc.). So I reimplemented some messages handling (QWidget::nativeEvent ()) from Win32 API, in order to preserve the functionality of AeroSnap, and others.

      Although it works perfectly, when my custom window is transferred from one screen to another (I have two screens), a visual glitch appears (as you can see below). After the glitch appears, resizing the window, correct the bug. Moreover, after some debugging, I noticed that the Qt QWidget::geometry() is not the same as the geometry returned by Win32 API GetWindowRect() when the bug appears.

      The geometry reported initially by QWidget::geometry() is QRect(640,280 800x600), by QWidget::frameGeometry() is QRect(640,280 800x600), and by the Win32 GetWindowRect() is QRect(640,280 800x600). So, the same geometry. But, after the window is moved between the two monitors, the geometry reported by QWidget::geometry() becomes QGeometry(1541,322 784x561). The geometry reported by QWidget::frameGeometry() or GetWindowRect() is unchanged. Of course, after that, when the window is resized, the geometry is re correctly reported, and the painting issue dissapears and the widgets are re correctly positioned.

      So Qt do not report the correct geometry for a custom frameless window on Windows 10 compared to the geometry returned by the Win32 GetWindowRect() (which is the correct one), when the window is moved between two displays.

      Both the monitors have the same resolution, and none of them is HDPI.

      Minimal reproductible self-contained example:

      // main.cpp
      #include "MainWindow.h"
      
      #include <QtWidgets/qapplication.h>
      
      int main(int argc, char *argv[])
      {
          QApplication a(argc, argv);
          MainWindow w;
          w.show();
          return a.exec();
      }
      
      // MainWindow.h 
      
      #pragma once 
      
      #include <QtWidgets/qmainwindow.h> 
      
      class MainWindow : public QMainWindow { 
          Q_OBJECT 
      public: 
          MainWindow(QWidget *parent = Q_NULLPTR); 
      
      protected: 
          void paintEvent(QPaintEvent* event) override; 
          bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; };
      // MainWindow.cpp
      #include "MainWindow.h"
      
      #include <QtGui/qpainter.h>
      
      #include <Windows.h>
      #include <Windowsx.h>
      
      MainWindow::MainWindow(QWidget* parent)
          : QMainWindow(parent)
      {
          ::SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
      }
      
      void MainWindow::paintEvent(QPaintEvent* event)
      {
          QPainter painter(this);
      
          // Background
          painter.fillRect(rect(), Qt::red);
      
          // Border
          painter.setBrush(Qt::NoBrush);
          painter.setPen(QPen(Qt::blue, 1));
          painter.drawRect(rect.adjusted(0, 0, -1, -1));
      
          // Title bar
          painter.fillRect(QRect(1, 1, rect().width() - 2, 19), Qt::yellow);
      }
      bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result)
      {
          MSG* msg = reinterpret_cast<MSG*>(message);
      
          switch (msg->message)
          {
          case WM_NCCALCSIZE:
              *result = 0;
              return true;
          case WM_NCHITTEST: {
              *result = 0;
      
              RECT winrect;
              GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
      
              // Code allowing to resize the window with the mouse is omitted.
      
              long x = GET_X_LPARAM(msg->lParam);
              long y = GET_Y_LPARAM(msg->lParam);
              if (x > winrect.left&& x < winrect.right && y > winrect.top&& y < winrect.top + 20) {
                  // To allow moving the window.
                  *result = HTCAPTION;
                  return true;
              }
      
              repaint();
              return false;
          }
          default:
              break;
          }
      
          return false;
      }
      

      Attachments

        1. Capture.PNG
          Capture.PNG
          12 kB
        2. Capture 1.PNG
          Capture 1.PNG
          15 kB
        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
            hgruniaux Hubert Gruniaux
            Votes:
            2 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes