Home > database >  Flickering when resizing window from left side QT
Flickering when resizing window from left side QT

Time:10-14

I'm aware that have some similar questions about this topic, but I couldn't find one relative to a window being created using Qt.

I tried all suggestions in this answer: How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

  • Modifying WM_WINDOWPOSCHANGING flag to SWP_NOCOPYBITS
       wp = reinterpret_cast<tagWINDOWPOS*>(msg->lParam);
       wp->flags |= SWP_NOCOPYBITS;
  • Returning WVR_VALIDRECTS in WM_NCCALCSIZE
        if (msg->wParam == 1)
            return WVR_VALIDRECTS;
  • Applying CS_HREDRAW | CS_VREDRAW styles to the window
SetWindowLongPtr(HWND(winId()), GWL_STYLE, CS_HREDRAW | CS_VREDRAW | WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
  • Apply the exstyle WS_EX_COMPOSITED
  • WM_ERASEBKGND to return 1;

But the flicker persists. What else I could try?

The goal is to create a window without a caption, that can be resized/minimized.

The code below is working the problem is when the window is being resized from left/top it causes flickering.

//.h
#include <QtWidgets/QMainWindow>
#include <Windows.h>

class MainWindow : public QMainWindow
{
    Q_OBJECT


public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void Frameless()
    {
        // set flags that will override what Qt does, especially with the Qt::FramelessWindowHint which essentially disables WS_SIZEBOX/WS_THICKFRAME
        SetWindowLongPtr(HWND(winId()), GWL_STYLE, WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
    }

private:
    Ui::MainWindowClass ui;

protected:
    virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;

};
//.cpp
#include "MainWindow.h"


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
    Frameless();
    return;
}

MainWindow::~MainWindow()
{}

bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{

    MSG* msg = static_cast<MSG*>(message);

    switch (msg->message)
    {
    case WM_WINDOWPOSCHANGING:
    {
        tagWINDOWPOS* wp;
        wp = reinterpret_cast<tagWINDOWPOS*>(msg->lParam);
        wp->flags |= SWP_NOCOPYBITS;
    }
    break;

    case WM_NCCALCSIZE:
    {
        if (msg->wParam == 1)
            return WVR_VALIDRECTS;

        // Just return 0 and mark event as handled. This will draw the widget contents
        // into the full size of the frame, instead of leaving room for it.
        *result = 0;
        
        return true;
    }
    break;

    case 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;
    }
    break;

    default:
        return QWidget::nativeEvent(eventType, message, result);
    }


    return QWidget::nativeEvent(eventType, message, result);
}

EDIT

I was testing the GUI compiling the project as debug when I changed to release the flicker reduced a lot but still persists, and now it flickers from the right instead of the left.

Testing with QT 6.3.1 static debug:

https://www.youtube.com/watch?v=a5fmXKsKDaY&feature=youtu.be

Testing with QT 6.3.1 static release:

https://www.youtube.com/watch?v=OwpxmCsLLRQ

Computer settings, os version: https://i.imgur.com/zdDAP1D.png

I tested on two different machines using win10, and the flicker happens on both.

The same issue happens with the frameless.h from jdfa answer, the difference is that my method the GUI resizing is way more 'smoothly'/fast.

CodePudding user response:

I would suggest to avoid this kind of mix of Qt and WinAPI. Not only it is not portable, but it is also produce your issue.

This problem can be avoided by solving original task (frameless resizeble window) in Qt way by using Qt::FramelessWindowHint. Unfortunately, resizing functionality will be lost together with frame in that case. So you need to reimplement it by overriding mouse event handlers (mousePressEvent, mouseMoveEvent). You are basically doing similar thing already, just using WinAPI.

That problem is already solved here: https://gist.github.com/Nico-Duduf/b8c799f1f2a694732abd1238843b1d70

Here is my result with it: https://i.imgur.com/ovbG2hI.mp4

Edit: here is how I tested it:

#include <QApplication>

#include <QtWidgets/QMainWindow>

#include "FrameLess.h"

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget* parent = nullptr)
        : QMainWindow(parent)
    {
        setStyleSheet("border-image:url(\"bg.png\");");
    }
};

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    MainWindow main;
    FrameLess f(&main);
    main.show();

    return app.exec();
}

CodePudding user response:

I created a project in 6.4 that doesn't have the jittery effect you describe and showed in the video. I tested it on windows 10.

Please create a folder, let's call it TestImage, and save the following files:

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 Qt::WindowFlags flags;
 flags = Qt::Window;
 //flags |= Qt::FramelessWindowHint;
 flags |= Qt::CustomizeWindowHint;
 this->setWindowFlags(flags);
}

MainWindow::~MainWindow()
{
    delete ui;
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

TestImage.pro

QT        = core gui

greaterThan(QT_MAJOR_VERSION, 4): QT  = widgets

CONFIG  = c  17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES  = QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES  = \
    main.cpp \
    mainwindow.cpp

HEADERS  = \
    mainwindow.h

FORMS  = \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS  = target

RESOURCES  = \
    resources.qrc

earth.jpeg (save the image in the same folder)

resources.qrc

<RCC>
    <qresource prefix="/image">
        <file>earth.jpeg</file>
    </qresource>
</RCC>

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget  name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>2068</width>
    <height>1599</height>
   </rect>
  </property>
  <property name="sizePolicy">
   <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
    <horstretch>0</horstretch>
    <verstretch>0</verstretch>
   </sizepolicy>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget  name="centralwidget">
   <layout  name="gridLayout_2">
    <item row="0" column="0">
     <layout  name="gridLayout">
      <property name="sizeConstraint">
       <enum>QLayout::SetNoConstraint</enum>
      </property>
      <item row="0" column="0">
       <widget  name="label">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Ignored" vsizetype="Ignored">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="text">
         <string/>
        </property>
        <property name="pixmap">
         <pixmap resource="resources.qrc">:/image/earth.jpeg</pixmap>
        </property>
        <property name="scaledContents">
         <bool>true</bool>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget  name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>2068</width>
     <height>22</height>
    </rect>
   </property>
  </widget>
  <widget  name="statusbar"/>
 </widget>
 <resources>
  <include location="resources.qrc"/>
 </resources>
 <connections/>
</ui>
  •  Tags:  
  • c qt
  • Related