首页 > 解决方案 > 如何将悬停或鼠标区域更新事件传播到 QML 中的较低元素?

问题描述

我有一些具有半径属性的兄弟 Rectangle 元素,因此它们显示为圆形。每个都有一个具有子 MouseArea 的子 Item,该 Item 的目的是实现“圆形鼠标区域”效果(原始 SO 答案)。对 Item 和 MouseArea 进行了检测,以便单击和拖动只会在 Rectangle 的可见圆形内生效,而不是在作为 Rectangle 实际足迹的边界框内生效。

不幸的是,下面显示了一个故障。拖动点时的预期结果是圆圈 1 移动,这在大多数情况下都会发生。但是,当您创建圆 1 然后圆 2 然后将鼠标光标移动到点时,它不会发生。如果您这样做并尝试拖动或单击,您的交互将落入后台全窗口 MouseArea 并创建一个新圆圈。

两个部分重叠的圆圈,编号为 1 和 2,一个圆圈中的一个点靠近另一个圆圈

此问题的原因是当鼠标光标从圆圈#2 移动到点时,圆圈#1 的 MouseArea 的 mouseX 和 mouseY 没有得到更新。当圆 #2 允许点击向下传播时,它会击中圆 #1 的 Rectangle,但随后圆 #1 的 Item 声明 containsMouse 为 false 并再次向下传播。

一旦鼠标光标离开圆 #2 的边界矩形的足迹,例如从点向上或向左移动一点,圆 #1 的 MouseArea 就会更新,它的 containsMouse 变为 true,它开始再次捕获点击和拖动。

我已经尝试了一些潜在的解决方案,但并没有比下面的代码走得更远。

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
                }
            }
        }
    }
}

标签: qtqmlmouseeventqtquick2

解决方案


这不是您问题的确切解决方案,但这就是我克服问题根源的方法。

在我的应用程序中,有一个MouseArea与场景的大部分重叠的QQuickFrameBufferObject. 这是我绘制 3D 场景的地方。由于您无法QHoverEvent在 QML 中传播 a,因此您必须使用处理程序捕获位置更改信号onPositionChanged并调用 C++ 中的方法,该方法将 a 发送QHoverEvent到所需项目。

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);
    }
};

推荐阅读