Search code examples
qtqmlqt-quickqtquick2

Do not repaint window during resize


My QML application (Qt 5.4) is based on a Window item. The application can be resized by the user. When the application is resized the content of the application is being resized respectively (with onWidthChanged and onHeightChanged).

This is all fine.

But to avoid flickering I don't want to update the content of the application while the applicaiton is beeing resized. Is there a possibility in QML to detect when the user is actually resizing the window (holding the mouse button down over the border of the window) and don't recalculate the content before the resize is finished (the mouse button is released)?


Solution

  • EDIT : What Kuba Ober suggested is infinitely simpler and more robust, I'll still leave my answer here as I found it somewhat interesting (and the C++ custom component approach can be modified to filter the window events as suggested).


    Pardon me but I have written a quick and ugly hack to see if it was possible, it only covers the second part of your question (not updating the content). My solution blocks the repainting of an Item, but also hide it as soon as an update is requested on it (which may not be a problem for you).

    After reading the QQuickItem::updatePaintNode documentation and especially this phrase

    The function is called as a result of QQuickItem::update(), if the user has set the QQuickItem::ItemHasContents flag on the item.

    I created a C++ class to set/unset this flag on an abitrary QQuickItem :

    #ifndef ITEMUPDATEBLOCKER_H
    #define ITEMUPDATEBLOCKER_H
    
    #include <QObject>
    #include <QQuickItem>
    
    
    class ItemUpdateBlocker : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged)
        QQuickItem* m_target;
    
    public:
        explicit ItemUpdateBlocker(QObject *parent = 0) : QObject(parent), m_target(nullptr) {  }
        QQuickItem* target() const { return m_target; }
    
    signals:
        void targetChanged();
    
    private:
        static void blockUpdate(QQuickItem* target)
        {
            if (target)
                target->setFlag(QQuickItem::ItemHasContents, false);
        }
    
        static void unblockUpdate(QQuickItem* target)
        {
            if (target)
            {
                target->setFlag(QQuickItem::ItemHasContents, true);
                target->update();
            }
        }
    
    
    public slots:
        void setTarget(QQuickItem* target)
        {
            if (m_target == target)
                return;
            unblockUpdate(m_target);
            blockUpdate(target);
            m_target = target;
            emit targetChanged();
        }
    };
    
    #endif // ITEMUPDATEBLOCKER_H
    

    next step is to register this class so that it can be used in QML :

    qmlRegisterType<ItemUpdateBlocker>("com.mycompany.qmlcomponents", 1, 0, "ItemUpdateBlocker");
    

    And you can use it in QML like this :

    import QtQuick 2.4
    import QtQuick.Controls 1.3
    import QtQuick.Window 2.2
    import com.mycompany.qmlcomponents 1.0
    
    ApplicationWindow {
    
        width: 640
        height: 480
        visible: true
    
        Rectangle {
            color: "red"
            id: root
            anchors.fill: parent
    
            Text {
                text: blocker.target ? "Blocked" : "Not Blocked"
            }
    
            Rectangle {
                color: "white"
                anchors.centerIn: parent
                width: parent.width/2
                height: parent.height/2
    
                ItemUpdateBlocker {
                    id: blocker;
                }
    
                MouseArea {
                    anchors.fill: parent
                    onClicked: blocker.target = blocker.target ? null : parent
                }
            }
        }
    }
    

    You can of course add an active property to the blocker to simplify it's use (prettier than using a null target to disable it), but I'll leave that as an exercise.

    Maybe you can use that with a timer started whenever the width or the height of your Window is changed, I have not yet found a direct way to find if a window is resized.