Qt: Track mouse position while QDrag is running

Viewed 801

I am developing a Qt application with multiple windows and want to implement cross-window drag&drop functionality for some elements in my program.

To do so, I attach an event filter to the to-be-dragged QML elements and listen for the MousePress/MouseMove events to start the drag procedure as follows:

QDrag *drag = new QDrag(quickItem);
QMimeData* mimeData = new QMimeData();
mimeData->setText("Test");
drag->setHotSpot(QPoint(0, 0));
drag->setMimeData(mimeData);
drag->exec();

This works fine, but now I would like to show a little tooltip (being a QWidget) while dragging, following the mouse cursor and displaying a short text depending on the element the mouse is currently over (similar to the "Copy to ..." or "Move to..." labels appearing when you drag files around in Windows Explorer).

However, while dragging the element, I don't receive any MouseMove events neither on the QDrag object nor on the quickItem itself, which makes it impossible to track the mouse position. Since the mouse is grabbed during dragging, there should be some event in Qt that frequently reports the mouse position, no matter where on the screen the mouse is.

I am aware of the QDrag::setPixmap method, however this won't allow me to change my tooltip text during dragging and has some other limitations I would like to avoid.

Is there some way to listen to mouse move events while QDrag is running, without using platform-specific system APIs?

1 Answers

Update:

I don't think that there is way of doing this without using OS libraries nor getting the mouse position every X miliseconds. It looks like a really specific problem that Qt framework does not contemplate. You will need to write your own class to control this using win32 for windows, x11 for linux and the equivalent of Mac.

If you want to get the mouse position when your window is active and you are dragging something, check this:

Searching a bit I've found a solution for getting it when your window has the focus using QObject::eventFilter.

Create a class (for example EventListener) that inherits from QObject and overrides eventFilter and a method to set this as your qml window (which inherits from QObject) event filter with installEventFilter.

eventslistener.h:

#include <QEvent>
#include <QObject>
#include <QDebug>
#include <QDropEvent>

class EventsListener : public QObject
{
    Q_OBJECT

public:
    EventsListener(QObject * ptr) : QObject (ptr) {
    }

    Q_INVOKABLE void handleEventsOf(QObject *object) {
        if (object)
            object->installEventFilter(this);
    }

    bool eventFilter(QObject *object, QEvent *event) override {
        if(event->type() == QEvent::DragMove) {
            QDragMoveEvent *mouseEvent = static_cast<QDragMoveEvent*>(event);

            qDebug() << "Mouse position dragging (x, y): (" << mouseEvent->pos().x() << ", " << mouseEvent->pos().y() << ")";
            return false; //this is must return false or drop event will be handled by this method and drag&drop won't work correctly
        }

        return false;
    }
};

Now we need to access to an instance (singleton in this case) of this class with qmlRegisterSingletonType. You may wish to use qmlRegisterType instead to register this eventlistener as a type (instead of a singleton) and use signals to notify directly qml the mouse position.

main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "eventlistener.h"

static QObject *eventsListenerInstance(QQmlEngine *qmlEngine, QJSEngine *engine)
{
    return new EventsListener(engine);
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterSingletonType<EventsListener>("AppEventListener", 1, 0, "EventsListener", eventsListenerInstance);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml:

import ...
import AppEventListener 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    id: item
    property string display
    property alias dropEnabled: acceptDropCB.checked
    color: dropArea.containsDrag ? "#CFC" : "#EEE"

    Component.onCompleted: EventsListener.handleEventsOf(item)

...
}
Related