How to change cursor shape in qml when MouseArea is covered by another MouseArea
There are two MouseAreas with two different functions, one (green) partially on top of the other (red). I would like to change the shape of the cursor when the red MA is hovered (even if it is under the green MA), and I would like the green MA to react when pressed and nothing else.
The two MAs might be in different files, so I don't want to make explicit dependencies between them, like setting the correct cursorShape to green when containsMouse changes to red. Is there a way to prevent the green MouseArea from using the cursor shape?
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!")
}
}
source to share
There is no way to do this with properties MouseArea
or any other out-of-the-box solution. MouseArea
always sets some cursor shape - if the cursorShape property is not specified than the default ( Qt.ArrowCursor
) you can of course use mapToItem()
/ mapFromItem()
to get around this problem (as Mitch suggested). But there are other possibilities:
You can temporarily change visible
on the false
overlaid mouse area.
Alternatively, if both MouseArea
are siblings, you can use the property z
to get a specific object hierarchy that suits your needs.
source to share
I don't think this is possible, at least from QML. The green area of ββthe mouse has no hoverEnabled
set to true, so you won't get any position changes.
The best way to approach this problem is to use a large MouseArea
one that fills the maximum area of ββinterest to you, and use mapToItem () / mapFromItem () to translate the global mouse position to local coordinates for comparison with each mouse area:
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!")
}
}
As you can see, the green mouse area is no longer the mouse area. It seems that a mouse region that has a higher stacking order than another mouse region will block position changes for the lower mouse region, even if the higher one does not have hoverEnabled
it set to true.
Also note that this would be a little more concise if it weren't for QTBUG-41452 . Namely, you can shorten the expression containsMouse
:
readonly property bool containsMouse: contains(mapFromItem(globalMouseArea, globalMouseArea.mouseX, globalMouseArea.mouseY))
If you're worried about duplicating code here, you can use the function instead:
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!")
}
}
I used the property for the green mouse area as it would call twice otherwise containsMouse()
, which was wasteful.
source to share
You can create your own CusorShapeArea as shown below. This will help you control the shape of the cursor more easily.
First, create a C ++ CursorShapeArea class:
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;
}
Then register this type qml:
#include <QtDeclarative>
#include "cursorshapearea.h"
int main(int argc, char **argv)
{
...
qmlRegisterType<CursorShapeArea>("MyTools", 1, 0, "CursorShapeArea");
...
}
Then use it in QML, in your problems:
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
}
}
source to share