[SOLVED] How do I get the mouse position in a flickable after flicking?



  • I have a Flickable which contains a MouseArea. One of the MouseArea's jobs is to keep track of the cursor position of the mouse and, for example, display this on the screen.

    Here's my problem: How can I correctly update the cursor position after flicking?

    Here's the (simplified) MouseArea:

    MouseArea {
        id: myMouseArea
        property point cursorPos
        property bool cursorPosActive : false
        function updateCursorPos() { // doesn't work!!
            // here: mouseX/Y are not up-to-date !!
            cursorPos = Qt.point(mouseX, mouseY)
        }
        anchors.fill: parent
        hoverEnabled: true
        onClicked: {  // MouseEvent OK
            cursorPos = Qt.point(mouse.x, mouse.y)
        }
        onEntered: {  // here: mouseX/Y OK?
            cursorPos = Qt.point(mouseX, mouseY)
            cursorPosActive = true
        }
        onExited: { cursorPosActive = false }
        onPositionChanged: {  // MouseEvent OK
            cursorPos = Qt.point(mouse.x, mouse.y)
        }
        onWheel: {  // WheelEvent OK
            cursorPos = Qt.point(wheel.x, wheel.y)
            wheel.accepted = false; // pass to Flickable
        }
    }
    

    It looks comprehensive, but if I flick with the mouse wheel (otherwise keeping the mouse still!), the mouseX and mouseY values are still old values from the most previous mouse move (or click). That means that calling updateCursorPos(), e.g. by detecting movementEnded() in the Flickable, does not work as hoped:

    onMovementEnded : { myMouseArea.updateCursorPos() }

    [BTW: The WheelEvent from the wheel() signal correctly passes the cursor position in mouse.x and mouse.y, but the position in mouseX and mouseY were even older, i.e. it was not updated by the WheelEvent, probably because I needed to set accepted=false.]

    Is there a workaround to determine this "new" mouse position?

    More to the point: Is this a bug? Should flicking a MouseArea under the mouse infer an update of mouseX and mouseY? Or at least result in positionChanged() signals? Or is this a (desired) performance issue/limitation?

    Thanks for any help you can give! - momo



  • No replies yet to my first forum question. Anyone out there who can answer this?



  • @momo I had a similar, perhaps even more tricky problem, because I combine a Flickable with a Pincharea to also zoom into the map. This "map" is actually composed out of png-files with 1024x1024 px, which are arranged by means of two repeaters - one for the columns and inside of that another one for the rows.

    Maybe the following JavaScript snippet helps you:

    function calcXandY(x, y) {
        var x_new = (x + r_num * 1024 * pinchArea.zoomFactor) / pinchArea.zoomFactor * Globals.MOUSESCALING
        var y_new = (y + c_num * 1024 * pinchArea.zoomFactor) / pinchArea.zoomFactor * Globals.MOUSESCALING
        return [x_new,y_new]
    }
    function processMouseClicked(mouse) {
        markerDialog.visible = false
        console.log("c_num:", c_num, "r_num:", r_num, "mouseX:", mouse.x, "mouseY:", mouse.y);
        var realXY = calcXandY(mouse.x, mouse.y)
        var station = eventModel.stationForTouchArea(realXY[0], realXY[1], Globals.LANG) 
        if (station === "undefined") {
            console.log("No station found")
            markerDialog.visible = false
        }
        else {
            stationListView.model = eventModel.entriesForStation(station, Globals.LANG)
            markerDialog.setStationLabelText(station)
            markerDialog.x = stationListView.model[0].x * pinchArea.zoomFactor / Globals.MOUSESCALING - Globals.MARKERXOFFSET
            markerDialog.y = stationListView.model[0].y * pinchArea.zoomFactor / Globals.MOUSESCALING - markerDialog.height + Globals.MARKERYOFFSET
          stationListView.changed();
          markerDialog.visible = true
    }
    

    Sorry for the Globals stuff and be aware that this is surely not efficient code. Anyway, I would be glad if it helped.

    Best,
    wumpus



  • If the size of the flickable is bigger than your screen then mouseX and mouseY are relative to the screen size not the flickable. If you still want to get relative to the flickable then you would have to combine onMovementEnded with contentX and contentY properties from flickable.

    After flicking your x and y position should be x = contentX + mouseX and the same for y.

    Hope this helps.

    Regards



  • @nedo99: A big thanks for the tip! That in fact solved my problem, but maybe not in the way you might have thought! I finally got it to work this way:

    MouseArea {
        id: myMouseArea
        property point cursorPos
        property bool cursorPosActive : false
    
        // new code to handle flicking
        property point startFlickContentRef: Qt.point(0, 0)
        property point startFlickMousePos: Qt.point(mouseX, mouseY)
        property bool flicking: false
        function setStartFlickMousePos(x,y) {
            // "prepare" for flicking whenever we have a legitimate mouse pos...
            if (!flicking)  // ...but are not actually flicking!
                startFlickMousePos = Qt.point(x,y)
        }
        function startFlick(refPt) {
            // Note: startFlick() might be called multiple times before endFlick() is called...
            if (!flicking) {  // ...so this is an important check!
                startFlickContentRef = refPt
                flicking = true
            }
        }
        function endFlick(refPt) {
            if (flicking) {
                var diffX = refPt.x - startFlickContentRef.x
                var diffY = refPt.y - startFlickContentRef.y
                statBar.cursorPos = 
                    Qt.point(startFlickMousePos.x + diffX,
                             startFlickMousePos.y + diffY)
                flicking = false
            }
        }
    
        anchors.fill: parent
        hoverEnabled: true
        onClicked: {  // MouseEvent OK
            cursorPos = Qt.point(mouse.x, mouse.y)
            setStartFlickMousePos(mouse.x, mouse.y)
        }
        onEntered: {  // here: mouseX/Y OK?
            cursorPos = Qt.point(mouseX, mouseY)
            setStartFlickMousePos(mouseX, mouseY)
            cursorPosActive = true
        }
        onExited: { cursorPosActive = false }
        onPositionChanged: {  // MouseEvent OK
            cursorPos = Qt.point(mouse.x, mouse.y)
            setStartFlickMousePos(mouse.x, mouse.y)
        }
        onWheel: {  // WheelEvent OK
            cursorPos = Qt.point(wheel.x, wheel.y)
            setStartFlickMousePos(wheel.x, wheel.y)
            wheel.accepted = false; // pass to Flickable
        }
    }
    

    Then I added the following code to my Flickable:

        onFlickStarted: {
            myMouseArea.startFlick(Qt.point(contentX, contentY))
        }
        onFlickEnded: {
            myMouseArea.endFlick(Qt.point(contentX, contentY))
        }
    

    Note that it turned out to be important to use onFlickStarted/Ended instead of onMovementStarted/Ended. Also it's important to update the startFlickMousePos at every opportunity, because at the point where I normally would want to set it (in startFlick()), I might not have a valid mouseX and mouseY (that was the reason for this post in the first place!).

    Again thanks! Together we are strong!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.