Assumptions:
This only affects the edge case, that both, the cursor and the MouseArea
are moving.
My Assumption here is, that the movement of the cursor is handled before the movement of the MouseArea
. I don't have any definite proof for this. Only my test with the solution below, suggests that.
Solution
The first challenge is to detect movement of the MouseArea
. It might be that it moves, without its own x
and y
-values changing, e.g. if its parent is moving.
To solve this, I'll introduce two properties globalX
and globalX
. Then I use the trick from this answer on how to track a gobal position of an object.
Now I'll have two signals to handle: globalXChanged
and globalYChanged
.
According to my assumption, they are fired, after the mouseXChanged
and mouseYChanged
. I will use a flag isEntered
to make sure, I only handle one of them, by setting it to true
, if the first of them is fired.
I will use the cursor position on a globalMouseArea
to determine, whether the cursor is within bounds of the MouseArea
. This requires, the cursor is not in some other MouseArea
at that time, or at least I know of it
-> With this I already succeeded in detecting the entrance.
The second challenge is to detect the exit. Here we have 4 cases to distinguish:
- Cursor enters and leaves the
MouseArea
because of it's movement.
- Cursor enters and leaves the
MouseArea
because of the movement of the MouseArea
- Cursor enters because the
MouseArea
moves, and leaves, because the cursor moves
- Cursor enters because it moves, and leaves as the
MouseArea
moves away.
The first would be easy to handle. After it enters we handle entered
and when it leaves we handle exited
. But after the fix, mentioned by Mitch we can't rely on this anymore.
So we will not set hoverEnabled: true
and map the position of the cursor to the targetMouseArea
whenever either the cursor moves, or the targetMouseArea
moves, and act accordingly.
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
width: 400; height: 450
MouseArea {
id: globalMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: ani.restart()
}
Rectangle {
x: 300
y: 300
width: 50
height: 50
color: 'green'
}
Rectangle {
id: rect
width: 50
height: 50
color: 'red'
Text {
text: targetMouseArea.isEntered.toString()
}
MouseArea {
id: targetMouseArea
anchors.fill: parent
signal enteredBySelfMovement
signal enteredByMouseMovement
onEnteredByMouseMovement: console.log('Cause: Mouse')
onEnteredBySelfMovement: console.log('Cause: Self')
property point globalPos: {
var c = Qt.point(0, 0)
var itm = this
for (; itm.parent !== null; itm = itm.parent) {
c.x += itm.x
c.y += itm.y
}
return c
}
property bool isEntered: false
function checkCollision(sig) {
if ((globalPos.y < globalMouseArea.mouseY)
&& (globalPos.y + height > globalMouseArea.mouseY)
&& (globalPos.x < globalMouseArea.mouseX)
&& (globalPos.x + width > globalMouseArea.mouseX)) {
if (!isEntered) {
isEntered = true
sig()
}
}
else if (isEntered && !containsMouse) {
console.log(isEntered = false)
}
}
onGlobalPosChanged: {
checkCollision(enteredBySelfMovement)
}
Connections {
target: globalMouseArea
onPositionChanged: {
targetMouseArea.checkCollision(targetMouseArea.enteredByMouseMovement)
}
}
}
}
NumberAnimation {
id: ani
target: rect
properties: 'x,y'
from: 0
to: 300
running: true
duration: 10000
}
}
Problems left: When we clicked within the targetMouseArea
, as long as a button is pressed, we won't detect the leaving.