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!")
    }
}

      

+3


source to share


3 answers


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.

+3


source


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.

+2


source


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

      

+1


source







All Articles