使用 hoverEnabled 在嵌套的 MouseArea 中获取鼠标 x 和 y

Get mouse x & y in nested MouseArea with hoverEnabled

我制作了一个简化的 MapImage 组件,它允许使用鼠标缩放和平移图像。该组件使用 Flickable and MouseArea 个组件。 MapImage 组件只处理图像显示、缩放和平移。我想在 main.qml 中的 MapImage 实例中使用另一个 MouseArea(以便能够使用 Canvas 放置对象,但这在这里并不重要)。这不是 MapImage 的工作,所以我真的需要第二个 MouseArea 组件。

我需要设置取自 updateFlickable 函数的 hoverEnabled property to true because I need onPositionChanged and others events... But this property seems to cause problems with mouseX and mouseY 值。当我使用鼠标滚轮缩放时,缩放不会发生在鼠标位置...

我在此处或 gist 中提供了一个最小示例。

有解决这个问题的提示吗?

main.qml

import QtQml.Models 2.11
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11

MapImage {
    id: map
    height: 600
    width: 800

    imageSource: "https://images.unsplash.com/photo-1651634099253-720df02a0d50"

    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton
        hoverEnabled: true // this is required to use onPositionChanged
        preventStealing: false

        onPressed: {
            // needed for flickable
            mouse.accepted = false;
        }

        onPositionChanged: {
            // do something.
        }

    }
}

MapImage.qml

import QtQuick 2.11

Item {
    id: root
    property alias imageSource: image.source

    Flickable {
        id: flickable
        anchors.fill: parent
        contentWidth: props.originalImageWidth
        contentHeight: props.originalImageHeight

        Image {
            id: image
            fillMode: Image.PreserveAspectFit

            onStatusChanged: {
                if (status === Image.Ready) {
                    props.originalImageWidth = sourceSize.width;
                    props.originalImageHeight = sourceSize.height;
                    props.changeCurrentScale(1);
                }
            }

            // define the image display size
            width: flickable.contentWidth;
            height: flickable.contentHeight;

            MouseArea {
                id: mouseArea
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton
                hoverEnabled: true

                onWheel: {
                    wheel.accepted = false;
                    props.changeCurrentScale(wheel.angleDelta.y);
                }
            }
        }

        QtObject {
            id: props

            // original image size
            property int originalImageWidth
            property int originalImageHeight

            property real scaleStep: 0.2
            property real currentScale: 0.1

            onCurrentScaleChanged: updateFlickable(currentScale);

            function updateFlickable(scale) {
                console.log(mouseArea.mouseX, mouseArea.mouseY); // <------ I am no longer able to get mouse x and y coordinates
                flickable.resizeContent(originalImageWidth * scale, originalImageHeight * scale, Qt.point(mouseArea.mouseX, mouseArea.mouseY));
                flickable.returnToBounds();
            }

            function changeCurrentScale(wheelDelta) {
                if (wheelDelta > 0) currentScale = currentScale * (1 + scaleStep);
                else currentScale = currentScale / (1 + scaleStep);
            }
        }
    }
}

终于找到解决办法了。我必须在我的 MapImage 组件中添加一个新的 属性。这个属性的作用是在父坐标系中存储鼠标在父鼠标区域的更新位置。之后,我必须使用 mapToItem 在 flickable.contentItem 坐标系中进行转换。

main.qml

import QtQml.Models 2.11
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11

MapImage {
    id: map
    height: 600
    width: 800

    imageSource: "https://images.unsplash.com/photo-1651634099253-720df02a0d50"

    MouseArea {
        anchors.fill: parent
        acceptedButtons: Qt.LeftButton
        hoverEnabled: true // this is required to use onPositionChanged
        preventStealing: false

        onPressed: {
            // needed for flickable
            mouse.accepted = false;
        }

        onPositionChanged: {
            // CHANGE HERE
            // the position must be updated on every position change
            map.parentMouseAreaPosition = Qt.point(mouse.x, mouse.y);
            // TO HERE
        }
    }
}

MapImage.qml

import QtQuick 2.11

Item {
    id: root
    property alias imageSource: image.source

    // CHANGE HERE
    // the current mouse position in the parent mouse area in parent coordinate system
    property var parentMouseAreaPosition: Qt.point(0, 0)

    // this function maps the parent coordinate system to that of contentItem
    function __mapToContentItem(x, y) {
        return mapToItem(flickable.contentItem, x, y);
    }
    // TO HERE

    Flickable {
        id: flickable
        anchors.fill: parent
        contentWidth: props.originalImageWidth
        contentHeight: props.originalImageHeight

        Image {
            id: image
            fillMode: Image.PreserveAspectFit

            onStatusChanged: {
                if (status === Image.Ready) {
                    props.originalImageWidth = sourceSize.width;
                    props.originalImageHeight = sourceSize.height;
                    props.changeCurrentScale(1);
                }
            }

            // define the image display size
            width: flickable.contentWidth;
            height: flickable.contentHeight;

            MouseArea {
                id: mouseArea
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton
                hoverEnabled: true

                onWheel: {
                    wheel.accepted = false;
                    props.changeCurrentScale(wheel.angleDelta.y);
                }
            }
        }

        QtObject {
            id: props

            // original image size
            property int originalImageWidth
            property int originalImageHeight

            property real scaleStep: 0.2
            property real currentScale: 0.1

            onCurrentScaleChanged: updateFlickable(currentScale);

            function updateFlickable(scale) {
                // CHANGE HERE
                // get the mapped point
                let point = __mapToContentItem(root.parentMouseAreaPosition.x, root.parentMouseAreaPosition.y);
                console.log(point.x, point.y);
                flickable.resizeContent(originalImageWidth * scale, originalImageHeight * scale, point);
                // TO HERE
                flickable.returnToBounds();
            }

            function changeCurrentScale(wheelDelta) {
                if (wheelDelta > 0) currentScale = currentScale * (1 + scaleStep);
                else currentScale = currentScale / (1 + scaleStep);
            }
        }
    }
}

不知道有没有更好的解决办法