3

This issue happens on Windows, but not on Linux. I haven't tried any other platforms.

I have a custom class (code below) that uses QCursor to set the mouse position.

The issue is with the following code (repo):

import QtQuick 2.15
import QtQuick.Window 2.15

// Custom C++ class, implementation below
import io.github.myProject.utilities.mousehelper 1.0

Window {
    visible: true
    width: 800
    height: 600

    MouseHelper { id: mouseHelper }

    MouseArea {
        id: mouseArea
        hoverEnabled: true
        anchors.fill: parent
        property var p

        onPressed: {
            p = mouseArea.mapToGlobal(
                mouseArea.width * 0.5, mouseArea.height * 0.5);
            mouseHelper.setCursorPosition(0, 0);
        }

        onReleased: {
            mouseHelper.setCursorPosition(p.x, p.y);
        }

        onExited: {
            console.log('This should happen twice, but it only happens once.');
        }
    }
}

Steps to reproduce the issue:

  1. Mouse down on the window. The cursor will move to the top-left of the screen, and onExited will fire.
  2. Release the mouse button. The cursor will jump to the middle of the window.
  3. Move the mouse out of the window.

onExited should fire a second time when the user moves the mouse out of the window, but it doesn't. Is there some way I can either

  1. cause it to fire, or
  2. otherwise detect that the mouse has moved out of the mouse area?

onPositionChanged still fires, but I can only use this to detect when the mouse is close to the edge of the MouseArea, not when it has left.

I tried overlaying a global MouseArea on top and passing all events through as a way to do some manual special-case position checking, but I couldn't pass hover events through.


The class for setting the mouse position:

#ifndef MOUSEHELPER_H
#define MOUSEHELPER_H

#include <QObject>
#include <QCursor>

class MouseHelper : public QObject {
    Q_OBJECT
public:
    explicit MouseHelper(QObject *parent = nullptr);

    Q_INVOKABLE void setCursorPosition(int x, int y);

signals:

public slots:
};

#endif // MOUSEHELPER_H
#include "mousehelper.h"
#include <QGuiApplication>

MouseHelper::MouseHelper(QObject *parent) : QObject(parent) {}

void MouseHelper::setCursorPosition(int x, int y) {
    QCursor::setPos(x, y);
}

I register this class as a type with QML in my main function:

int main(int argc, char *argv[]) {
    // ...
    qmlRegisterType<MouseHelper>("io.github.myProject.utilities.mousehelper",
                                 1, 0, "MouseHelper");
}

I can then import it in QML and use it.

Joshua Wade
  • 4,755
  • 2
  • 24
  • 44
  • The code you are showing appears to be programmatically entering area2, not exiting. I must be missing something. – JarMan Aug 22 '20 at 14:19
  • @JarMan Sorry! It is programmatically entering `area2`. The issue is, I have no way of detecting when the user moves the mouse out of `area2`, because the `onExited` event only fires if the user moved the mouse into `area2` like normal - I guess moving it programmatically doesn't "count", and so it doesn't know to track the `onExited`?. – Joshua Wade Aug 24 '20 at 02:18
  • @JarMan I've edited the question to hopefully make it more clear. – Joshua Wade Aug 24 '20 at 02:28
  • @JoshuaWade Explain yourself better, your question is confusing as they already pointed it out to you (it also had a TYPO). Could you indicate in detail what should happen. According to your code, when area1 is pressed then the cursor jumps to the middle of area 2, so according to that code it does not leave area2 but from area1 to area2 – eyllanesc Aug 24 '20 at 03:06
  • @eyllanesc My apologies, I've edited the question with steps to cause the issue. 1) Click on `area1`, 2) move the mouse out of `area2`. `onExited` should fire on `area2`, but it doesn't. – Joshua Wade Aug 24 '20 at 03:13
  • @JoshuaWade Using Qt 5.15 on Linux does trigger onExited. – eyllanesc Aug 24 '20 at 03:15
  • @JoshuaWade Note: change `mouseHelper.setCursorPosition(area2.x + area2.width * 0.5, area2.height * 0.5);` to `var p = area2.mapToGlobal(area2.width * .5, area2.height * 0.5) mouseHelper.setCursorPosition(p.x, p.y);` since QCursor uses global positions. – eyllanesc Aug 24 '20 at 03:18
  • @eyllanesc I owe you an apology. I didn't fully understand my production issue. I should have tested my example, but I didn't and that wasted your time. I bought you some coffee... hope that makes up for it at least a little. – Joshua Wade Aug 24 '20 at 04:09
  • I've updated my example to reproduce the issue I'm seeing in production, and it's an actual copy-paste from my example project (what I should have done in the first place). – Joshua Wade Aug 24 '20 at 04:10
  • Here's a repo with the code: https://github.com/SecondFlight/mousearea-test – Joshua Wade Aug 24 '20 at 04:16
  • I tried your code: 1> I clicked the window(800x600) when clicked it went to top-left and onExited fired. 2> when I release the mouse the cursor come to the center of window(800x600) 3>when I move cursor out of window onExited fired again. So whats the problem here? Explaining this kind of problems with a gif can be better also – Yunus Temurlenk Aug 26 '20 at 11:54
  • @YunusTemurlenk I'll add a gif when I get back home. What you described is correct. I'm on Windows... maybe this is platform-specific issue? – Joshua Wade Aug 26 '20 at 14:57
  • 1
    @JoshuaWade Yes no problem on linux, I tried in both linux and windows only occurs on windows. I ll check more about it. Also onEntered not working after click – Yunus Temurlenk Aug 26 '20 at 15:21
  • Thank you for checking! I'll update the question. – Joshua Wade Aug 26 '20 at 15:23
  • 1
    After digging around this seems like a bug. `containsMouse` also returning false after released event . You can find a tricky as answered below to solve this issue maybe. – Yunus Temurlenk Aug 27 '20 at 07:39
  • 1
    I'm unable to reproduce this problem on Windows using Qt 5.12.4. So it seems to be a regression bug. Have you considered creating a [bug report](https://bugreports.qt.io)? – m7913d Aug 28 '20 at 11:58
  • Ah, I'm on `5.15.0` and didn't think to test for regressions. I'll make a report soon. – Joshua Wade Aug 28 '20 at 15:17
  • I'm trying to find the first version with the regression. I can no longer reproduce this on any version of Qt. I'm so incredibly confused. I think I rebooted once in the last week?? I give up. I will make a report if I can reproduce it again. – Joshua Wade Sep 02 '20 at 02:25

1 Answers1

5

As a workaround for your problem you can use a Timer to reset the position of the mouse cursor.

Either in QML:

MouseArea {
...
    Timer {
        id: timer
        interval: 10
        repeat: false
        onTriggered: {
            mouseHelper.setCursorPosition(mouseArea.p.x, mouseArea.p.y)
        }
    }
    
    onReleased: {
        timer.start()
    }
...
}

Or in your MouseHelper class:

#include <QTimer>
...
void MouseHelper::setCursorPosition(int x, int y) {
    QTimer::singleShot(10, this, [x, y]() { QCursor::setPos(x, y); });
}

This works for me if the interval of the timer is not too small.

ptr
  • 316
  • 1
  • 3
  • This method seems to be flaky for small `MouseArea`s. If I click, then move the mouse, then release, then move the mouse again, the second `onRelease` will not fire if I'm too quick. My `MouseArea`s are 20 by 20 pixels. – Joshua Wade Aug 25 '20 at 01:18