Search code examples
qmlqtquick2qt-quick

Querying global mouse position in QML


I'm programming a small PoC in QML. In a couple of places in my code I need to bind to/query global mouse position (say, mouse position in a scene or game window). Even in cases where mouse is outside of MouseAreas that I've defined so far.

Looking around, the only way to do it seems to be having whole screen covered with another MouseArea, most likely with hovering enabled. Then I also need to deal with semi-manually propagating (hover) events to underlying mouseAreas..

Am I missing something here? This seems like a pretty common case - is there a simpler/more elegant way to achieve it?

EDIT: The most problematic case seems to be while dragging outside a MouseArea. Below is a minimalistic example (it's using V-Play components and a mouse event spy from derM's answer). When I click the image and drag outside the MouseArea, mouse events are not coming anymore so the position cannot be updated unless there is a DropArea below.

The MouseEventSpy is taken from here in response to one of the answers. It is only modified to include the position as parameters to the signal.

import VPlay 2.0
import QtQuick 2.0
import MouseEventSpy 1.0

GameWindow {
    id: gameWindow
    activeScene: scene
    screenWidth: 960
    screenHeight: 640

    Scene {
        id: scene
        anchors.fill: parent

        Connections {
            target: MouseEventSpy
            onMouseEventDetected: {
                console.log(x)
                console.log(y)
            }
        }

        Image {
            id: tile
            x: 118
            y: 190
            width: 200
            height: 200
            source: "../assets/vplay-logo.png"
            anchors.centerIn: parent

            Drag.active: mausA.drag.active
            Drag.dragType: Drag.Automatic

            MouseArea {
                id: mausA
                anchors.fill: parent
                drag.target: parent
            }
        }
    }
}

Solution

  • You can install a eventFilter on the QGuiApplication, where all mouse events will pass through.

    How to do this is described here

    In the linked solution, I drop the information about the mouse position when emitting the signal. You can however easily retrieve the information by casting the QEvent that is passed to the eventFilter(...)-method into a QMouseEvent and add it as parameters to the signal.

    In the linked answer I register it as singleton available in QML and C++ so you can connect to the signal where ever needed.


    As it is provided in the linked answer, the MouseEventSpy will only handle QMouseEvents of various types. Once you start dragging something, there won't be QMouseEvents but QDragMoveEvents e.t.c. Therefore you need to extend the filter method, to also handle those.

    bool MouseEventSpy::eventFilter(QObject* watched, QEvent* event)
    {
        QEvent::Type t = event->type();
        if (t == QEvent::MouseButtonDblClick
                || t == QEvent::MouseButtonPress
                || t == QEvent::MouseButtonRelease
                || t == QEvent::MouseMove) {
            QMouseEvent* e = static_cast<QMouseEvent*>(event);
            emit mouseEventDetected(e->x(), e->y());
        }
    
        if (t == QEvent::DragMove) {
            QDragMoveEvent* e = static_cast<QDragMoveEvent*>(event);
            emit mouseEventDetected(e->pos().x(), e->pos().y());
        }
        return QObject::eventFilter(watched, event);
    }
    

    You can then translate the coordinates to what ever you need to (Screen, Window, ...)