Thursday, December 17, 2015

Qt issue /problem with frameless window flickering on ubuntu

When using a frameless window in QML (prehaps directly from c++ as well) you need to manually allow dragging of the window. The first solution I found was like this:

Window  {
    id: appWindow
    flags: Qt.Window|Qt.FramelessWindowHint

    MouseArea {
        id: mouseRegion
        anchors.fill: parent;
        property variant clickPos: "1,1"

        onPressed: {
            clickPos  = Qt.point(mouse.x,mouse.y)
        }

        onPositionChanged: {
            var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
            appWindow.x += delta.x;
            appWindow.y += delta.y;
        }
    }
}

This worked on windows, but on Ubuntu the window flickered on Ubuntu due to a race condition:
Mouse move event A:
                Receive mouse position relative to app position Y
                Update app to position X

Mouse move event B:
                Receive mouse position relative to old app position Y (and not X as expected)
                Update app to position Z

The second effectively incorrectly does “double” the delta…


The fix is then to use a global cursor position as reference, so the new solution is then

Window  {
    id: appWindow
    flags: Qt.Window|Qt.FramelessWindowHint
    MouseArea {
        id: mouseRegion
        anchors.fill: parent;
        property variant clickPos: "1,1"
        property variant appStartPos: "1,1"

        onPressed: {
            // fetch global position due to bug on ubuntu
            clickPos = gApplicationController.getCursorPos()
            appStartPos = Qt.point(appWindow.x, appWindow.y)
        }

        onPositionChanged: {
            var newPos = gApplicationController.getCursorPos()
            var delta = Qt.point(newPos.x-clickPos.x, newPos.y-clickPos.y)
            appWindow.x = appStartPos.x+delta.x;
            appWindow.y = appStartPos.y+delta.y;
        }
    }
}

Note you need to set up a c++ class that gets the cursor position something like

class AppController : public QObject
{
    Q_OBJECT

    QQmlApplicationEngine m_engine;
public:
    Q_INVOKABLE QVariant getCursorPos();
}

AppController::AppController()
{
    m_engine.rootContext()->setContextProperty("gApplicationController", this);
}

QVariant AppController::getCursorPos()
{
    return QVariant(QCursor::pos());
}