问题
I have some sibling Rectangle elements with a radius property so they appear as circles. Each has a child Item that has a child MouseArea, the purpose of the Item being to implement a "round mouse area" effect (original SO answer). The Item and MouseArea are instrumented such that clicks and drags will only take effect within the visible circular shape of the Rectangle, not within the bounding box that is the real footprint of the Rectangle.
Unfortunately there is a glitch illustrated below. The desired outcome when dragging at the dot is for circle 1 to move, and this happens in most circumstances. However, it does not happen when you create create circle 1 then circle 2 then move your mouse cursor to the dot. If you do that and attempt to drag or click, your interaction will fall through to the background full-window MouseArea and create a new circle.
The cause of this problem is that when the mouse cursor moves to the dot from circle #2, the mouseX and mouseY for circle #1's MouseArea do not get updated. When circle #2 allows the click to propagate downward, it hits the Rectangle of circle #1 but then circle #1's Item claims containsMouse is false and it propagates downward again.
As soon as the mouse cursor leaves the footprint of circle #2's bounding rectangle, such as by moving a bit up or left from the dot, circle #1's MouseArea gets updated and its containsMouse becomes true and it starts capturing clicks and drags again.
I have tried a handful of potential solutions and not gotten much farther than the code below.
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
visible: true
width: 640
height: 480
property real spotlightRadius: 100
MouseArea {
visible: true
anchors.fill: parent
onClicked: {
spotlightComponent.createObject(parent, {
"x": x + mouseX - spotlightRadius,
"y": y + mouseY - spotlightRadius,
"width": spotlightRadius * 2,
"height": spotlightRadius * 2
})
}
}
Component {
id: spotlightComponent
Rectangle {
id: spotlightCircle
visible: true
x: parent.x
y: parent.y
width: parent.width
height: parent.height
radius: Math.max(parent.width, parent.height) / 2
color: Qt.rgba(Math.random()*0.5+0.5,Math.random()*0.5+0.5,Math.random()*0.5+0.5,0.5);
Item {
anchors.fill: parent
drag.target: parent
onDoubleclicked: parent.destroy()
onWheel: { parent.z += wheel.pixelDelta.y; currentSpotlight = parent }
property alias drag: mouseArea.drag
//FIXME when moving the mouse out of a higher element's containsMouse circle
// but still inside its mouseArea.containsMouse square, lower elements'
// mouseArea do not update, so their containsMouse doesn't update, so clicks
// fall through when they should not.
property bool containsMouse: {
var x1 = width / 2;
var y1 = height / 2;
var x2 = mouseArea.mouseX;
var y2 = mouseArea.mouseY;
var deltax = x1 - x2;
var deltay = y1 - y2;
var distance2 = deltax * deltax + deltay * deltay;
var radius2 = Math.pow(Math.min(width, height) / 2, 2);
return distance2 < radius2;
}
signal clicked(var mouse)
signal doubleclicked(var mouse)
signal wheel(var wheel)
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
//FIXME without acceptedButtons, propagated un-accepted clicks end up with the wrong coordinates
acceptedButtons: parent.containsMouse ? Qt.LeftButton : Qt.NoButton
propagateComposedEvents: true
onClicked: { if (parent.containsMouse) { parent.clicked(mouse) } else { mouse.accepted = false } }
onDoubleClicked: { if (parent.containsMouse) { parent.doubleclicked(mouse) } }
onWheel: { if (parent.containsMouse) { parent.wheel(wheel) } }
drag.filterChildren: true
}
}
}
}
}
回答1:
This is not the exact solution for your problem, but this is how I overcame the root of the issue.
In my application there is a MouseArea
that overlaps a large chunk of the scene which is a QQuickFrameBufferObject
. This is where I draw the 3D scene. Since you cannot propagate a QHoverEvent
in QML, you will have to catch the position changed signal using the onPositionChanged
handler and invoke a method in C++ which will send a QHoverEvent
to the required items.
QML:
MouseArea {
onPositionChanged: {
model.sendHoverEvent(Qt.point(mouse.x, mouse.y))
}
}
C++:
class TreeViewModel : public QAbstractListModel
{
// ...
void TreeViewModel::sendHoverEvent(QPointF p) {
QHoverEvent hoverEvent(QEvent::HoverMove, p, p);
QApplication::sendEvent(mApplication.graphicsLayer(), &hoverEvent);
}
};
回答2:
You need to reject the pressed
event of your underlying MouseArea
. It should be enough to solve your problems. If the pressed event is rejected, the click will automatically be forwarded to the underlying sibling items. propagateComposedEvents
and filterChildren
are useless in your case.
Note that if the wheel event causes the z coordinate of your spotlightCircle to become less than 0, it will no longer accept mouse event since they will be caught by the "Creation" MouseArea
import QtQuick 2.10
import QtQuick.Controls 2.3
ApplicationWindow {
visible: true
width: 640
height: 480
property real spotlightRadius: 100
MouseArea {
visible: true
anchors.fill: parent
onClicked: {
spotlightComponent.createObject(parent, {
"x": x + mouseX - spotlightRadius,
"y": y + mouseY - spotlightRadius,
"width": spotlightRadius * 2,
"height": spotlightRadius * 2
})
}
}
Component {
id: spotlightComponent
Rectangle {
id: spotlightCircle
visible: true
x: parent.x
y: parent.y
width: parent.width
height: parent.height
radius: Math.max(parent.width, parent.height) / 2
color: Qt.rgba(Math.random()*0.5+0.5,Math.random()*0.5+0.5,Math.random()*0.5+0.5,0.5);
Item {
anchors.fill: parent
onDoubleClicked: parent.destroy()
onWheel: { parent.z += wheel.pixelDelta.y; currentSpotlight = parent }
signal clicked(var mouse)
signal pressed(var mouse)
signal doubleClicked(var mouse)
signal wheel(var wheel)
property alias drag: mouseArea.drag
property bool containsMouse: {
var x1 = width / 2;
var y1 = height / 2;
var x2 = mouseArea.mouseX;
var y2 = mouseArea.mouseY;
var deltax = x1 - x2;
var deltay = y1 - y2;
var distance2 = deltax * deltax + deltay * deltay;
var radius2 = Math.pow(Math.min(width, height) / 2, 2);
return distance2 < radius2;
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
drag.target: spotlightCircle
onPressed: { if (parent.containsMouse) { parent.pressed(mouse) } else { mouse.accepted = false } }
onClicked: { if (parent.containsMouse) { parent.clicked(mouse) } else { mouse.accepted = false } }
onDoubleClicked: { if (containsMouse2) { parent.doubleClicked(mouse) } }
onWheel: { if (parent.containsMouse) { parent.wheel(wheel) } }
}
}
}
}
}
来源:https://stackoverflow.com/questions/53826202/how-can-i-propagate-hover-or-mousearea-update-events-to-lower-elements-in-qml