c++qtqml

test dragging an object in QML with a mouse using QtTest module


I have a very simple QML scene with a blue square (size 100x100) placed in the top left corner (x = 0, y = 0). This square is draggable with a mouse.

import QtQuick
import QtQuick.Window

Window {
    id: box
    width: 500
    height: 500
    visible: true

    Rectangle {
        id: square
        objectName: "square"
        width: 100
        height: 100
        x: 0
        y: 0
        color: "blue"

        MouseArea {
            anchors.fill: parent
            drag.target: square
            drag.axis: Drag.XAndYAxis
        }
    }
}

Then I have a simple main.cpp in which I instantiate the scene. And then after some delay (100 ms) when the scene is ready, I simulate dragging the square with a mouse.

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QtTest>

void testMoving()
{
    auto window = QGuiApplication::focusWindow();
    Q_ASSERT(window);

    auto square = window->findChild<QQuickItem*>("square");
    Q_ASSERT(square);

    QTest::mousePress(window, Qt::LeftButton, {}, {50, 50});
    QTest::mouseMove(window, {250, 250});
    QTest::mouseRelease(window, Qt::LeftButton, {}, {250, 250});

    qDebug() << square->position();
}

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

    QQmlApplicationEngine engine;
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
        &app, []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    engine.loadFromModule("movable", "Main");

    QTimer::singleShot(100, &testMoving);

    return app.exec();
}

The code in testMoving() is supposed to simulate mouse press at coordinates (50, 50) and dragging to (250, 250). So the coordinates of the square should change from (0, 0) to (200, 200). But it does not work and the square is not moved. The output from qDebug() is QPointF(0,0).

I experimented with the code a bit, so I changed the lines to:

    QTest::mousePress(window, Qt::LeftButton, {}, {50, 50});
    QTest::mouseMove(window, {200, 200});
    QTest::mouseRelease(window, Qt::LeftButton, {}, {250, 250});

Now the result is surprisingly (for me) QPointF(50,50), which is the difference between those 200s and 250s. So the square moved. So I thought "aha!" I must first move the cursor to start of dragging and then release it at the target destination. So I tried:

    QTest::mousePress(window, Qt::LeftButton, {}, {50, 50});
    QTest::mouseMove(window, {50, 50});
    QTest::mouseRelease(window, Qt::LeftButton, {}, {250, 250});

... and the actual result is again QPointF(0,0) instead of the expected QPointF(200,200). WTH is going on? Has anyone an idea how to simulate dragging the square with a mouse using QtTest?


Solution

  • This comment actually made me realize that this is still a drag operation. And a drag operation requires the mouse to travel a certain minimum distance before it is registered as dragging. Obviously, the distance must be bigger than 10 pixels on his system.

    So the explanation is this:

    1. Mouse button press event gets processed at certain point X.
    2. Mouse move event to a point Y which is more distant from X than the minimum required distance (required to start the drag action).
    3. Mouse release at point Z of the final mouse destination is needed to finish the dragging.

    The final dragged distance then will be the distance between points Y and Z.

    PS: The minimum required distance can probably be obtained from https://doc.qt.io/qt-6/qstylehints.html#startDragDistance-prop