当 MouseArea 被另一个 MouseArea 覆盖时,如何在 qml 中更改光标形状

How to change cursor shape in qml when MouseArea is covered with another MouseArea

有两个具有两种不同职责的 MouseAreas,一个 (green) 部分位于另一个 (red) 之上。我想在 red MA 悬停时更改光标的形状(即使它在 green MA 下方),我想 绿色 MA 仅对压力做出反应。

两个 MA 可能在不同的文件中,所以我不想在它们之间建立明确的依赖关系,比如在 green 中设置适当的 cursorShape 每当在 red[ 中包含鼠标时=13=]变化。有没有办法阻止 green MouseArea 处理光标形状?

import QtQuick 2.4
import QtQuick.Window 2.2

Window {
    visible: true
    width: 200
    height: 200

    Rectangle { anchors.fill: parent; color: "yellow"; }

    MouseArea {
        width: 150
        height: 150
        hoverEnabled: true
        cursorShape: Qt.OpenHandCursor
        Rectangle { anchors.fill: parent; color: "red"; }
        onPositionChanged: console.log("position", mouse.x, mouse.y)
        onContainsMouseChanged: console.log("containsMouse", containsMouse)
    }
    MouseArea {
        x: 50
        y: 50
        width: 150
        height: 150
        hoverEnabled: false
        Rectangle { anchors.fill: parent; color: "green"; }
        onPressed: console.log("Ahoj!")
    }
}

我不认为这样做是可能的,至少在 QML 中是不可能的。绿色鼠标区域未将 hoverEnabled 设置为 true,因此您不会收到任何位置变化。

解决此问题的更好方法是使用更大的 MouseArea 填充您感兴趣的最大区域,并使用 mapToItem() / mapFromItem() 将全局鼠标位置转换为局部坐标与每个鼠标区域的比较:

import QtQuick 2.4
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 200
    height: 200

    Rectangle {
        anchors.fill: parent
        color: "yellow"
    }

    MouseArea {
        id: globalMouseArea
        anchors.fill: parent
        hoverEnabled: true
    }

    MouseArea {
        id: redMouseArea
        width: 150
        height: 150
        cursorShape: containsMouse ? Qt.OpenHandCursor : Qt.ArrowCursor
        enabled: false

        readonly property bool containsMouse: {
            var relativePos = mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY);
            return contains(Qt.point(relativePos.x, relativePos.y));
        }

        Rectangle {
            anchors.fill: parent
            color: "red"
        }
    }
    Rectangle {
        id: greenMouseArea
        x: 50
        y: 50
        width: 150
        height: 150
        color: containsMouse ? "brown" : "green"

        readonly property bool containsMouse: {
            var relativePos = mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY);
            return contains(Qt.point(relativePos.x, relativePos.y));
        }

        Connections {
            target: globalMouseArea
            onPressed: if (greenMouseArea.containsMouse) greenMouseArea.pressed()
        }

        signal pressed
        onPressed: console.log("Ahoj!")
    }
}

如您所见,绿色鼠标区域不再是鼠标区域。看起来堆叠顺序高于另一个鼠标区域的鼠标区域将阻止较低鼠标区域的位置更改,即使较高的鼠标区域没有 hoverEnabled 设置为 true。

另外请注意,如果不是 QTBUG-41452,它会稍微简洁一些。即,您可以缩短 containsMouse 表达式:

readonly property bool containsMouse: contains(mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY))

如果您担心这里的代码重复,那么您可以改用一个函数:

import QtQuick 2.4
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 200
    height: 200

    Rectangle {
        anchors.fill: parent
        color: "yellow"
    }

    MouseArea {
        id: globalMouseArea
        anchors.fill: parent
        hoverEnabled: true
    }

    function containsMouse(item) {
        var relativePos = globalMouseArea.mapToItem(item, globalMouseArea.mouseX, globalMouseArea.mouseY);
        return item.contains(Qt.point(relativePos.x, relativePos.y));
    }

    MouseArea {
        id: redMouseArea
        width: 150
        height: 150
        cursorShape: window.containsMouse(redMouseArea) ? Qt.OpenHandCursor : Qt.ArrowCursor
        enabled: false

        Rectangle {
            anchors.fill: parent
            color: "red"
        }
    }
    Rectangle {
        id: greenMouseArea
        x: 50
        y: 50
        width: 150
        height: 150
        color: containsMouse ? "brown" : "green"

        readonly property bool containsMouse: window.containsMouse(greenMouseArea)

        Connections {
            target: globalMouseArea
            onPressed: if (greenMouseArea.containsMouse) greenMouseArea.pressed()
        }

        signal pressed
        onPressed: console.log("Ahoj!")
    }
}

我为绿色鼠标区域使用了一个属性,否则它会调用containsMouse()两次,这是浪费。

MouseArea 属性或任何其他现成的解决方案都无法做到这一点。 MouseArea 总是设置一些光标形状——如果未指定 cursorShape 属性 则使用默认值 (Qt.ArrowCursor) 您当然可以使用 mapToItem()/mapFromItem() 来解决此问题(如 Mitch 所建议的)。但也有其他可能性:

您可以临时将覆盖鼠标区域visible更改为false
或者,如果 MouseArea 都是兄弟姐妹,您可以对 z 属性 进行操作以获得适合您需要的特定对象层次结构。

您可以创建自己的 CusorShapeArea,如下所示。它将帮助您更轻松地控制光标形状。

首先创建 C++ class CursorShapeArea:

cursorshapearea.h

#ifndef CURSORSHAPEAREA_H
#define CURSORSHAPEAREA_H

#include <QDeclarativeItem>

class QsltCursorShapeArea : public QDeclarativeItem
{
  Q_OBJECT

  Q_PROPERTY(Qt::CursorShape cursorShape READ cursorShape WRITE setCursorShape NOTIFY cursorShapeChanged)

public:

  explicit QsltCursorShapeArea(QDeclarativeItem *parent = 0);

  Qt::CursorShape cursorShape() const;
  Q_INVOKABLE void setCursorShape(Qt::CursorShape cursorShape);

private:
  int m_currentShape;

signals:
  void cursorShapeChanged();
};

#endif // CURSORSHAPEAREA_H

cursorshapearea.cpp

#include "cursorshapearea.h"

QsltCursorShapeArea::QsltCursorShapeArea(QDeclarativeItem *parent) :
  QDeclarativeItem(parent),
  m_currentShape(-1)
{
}

Qt::CursorShape QsltCursorShapeArea::cursorShape() const
{
  return cursor().shape();
}

void QsltCursorShapeArea::setCursorShape(Qt::CursorShape cursorShape)
{
  if (m_currentShape == (int) cursorShape)
    return;

  setCursor(cursorShape);
  emit cursorShapeChanged();

  m_currentShape = cursorShape;
}

然后注册这个qml类型:

#include <QtDeclarative>
#include "cursorshapearea.h"

int main(int argc, char **argv)
{
...
  qmlRegisterType<CursorShapeArea>("MyTools", 1, 0, "CursorShapeArea");
...
}

然后在 QML 中使用它,在您的问题中:

import QtQuick 1.1
import CursorShapeArea 0.1

Window {
visible: true
width: 200
height: 200

Rectangle { anchors.fill: parent; color: "yellow"; }

MouseArea {
    id: redArea
    width: 150
    height: 150
    hoverEnabled: true
    //cursorShape: Qt.OpenHandCursor
    Rectangle { anchors.fill: parent; color: "red"; }
    onPositionChanged: console.log("position", mouse.x, mouse.y)
    onContainsMouseChanged: console.log("containsMouse", containsMouse)
}
MouseArea {
    x: 50
    y: 50
    width: 150
    height: 150
    hoverEnabled: false
    Rectangle { anchors.fill: parent; color: "green"; }
    onPressed: console.log("Ahoj!")
} 
CursorShapeArea {
    anchors.fill: redArea
    cursorShape: Qt.OpenHandCursor
}
}

您可以将绿色鼠标区域的cursorShape设置为undefined,这样可以防止它改变光标。