Qt QML click under the bounding box

  • I'm trying to do the following.

    enter image description here

    So far the only solution I found is to create my button from C++ and to use setMask. Is there any other way in order to achieve the same results but just using QML?

    My shape button:

    import QtQuick 2.0
    import QtQuick.Shapes 1.14
    Item {
        id: sideButtonID
        property alias mouseX: mouseArea.mouseX
        property alias mouseY: mouseArea.mouseY
        readonly property bool pressed: containsMouse && mouseArea.pressed
        property point topLeftOffset: Qt.point(0, 0)
        property point topRightOffset: Qt.point(0, 0)
        property point bottomRightOffset: Qt.point(0, 0)
        property point bottomLeftOffset: Qt.point(0, 0)
        property var colorPressed: "green"
        property var colorSelected: "red"
        property var colorUnselected: "darkorange"
        signal clicked
        property var coordinate: generateButtonShapeCoordinates(x,y,width,height)
        property point topLeft: coordinate.topLeft
        property point topRight: coordinate.topRight
        property point bottomRight: coordinate.bottomRight
        property point bottomLeft: coordinate.bottomLeft
        function generateButtonShapeCoordinates(x, y, width, height){
            var topLeft = Qt.point(x-width/2 - topLeftOffset.x, y-height-topLeftOffset.y)
            var topRight = Qt.point(x+width/2-topRightOffset.x, y-height-topRightOffset.y)
            var bottomRight = Qt.point(x+width/2-bottomRightOffset.x, y-bottomRightOffset.y)
            var bottomLeft = Qt.point(x - width/2-bottomLeftOffset.x, y-bottomLeftOffset.y)
            return {
                topLeft : topLeft,
                topRight : topRight,
                bottomRight : bottomRight,
                bottomLeft : bottomLeft
        function inside(point, polygon) {
            // ray-casting algorithm based on
            // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
            var x = point[0], y = point[1];
            var inside = false;
            for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
                var xi = polygon[i][0], yi = polygon[i][1];
                var xj = polygon[j][0], yj = polygon[j][1];
                var intersect = ((yi > y) != (yj > y))
                        && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
                if (intersect) inside = !inside;
            return inside;
        property bool containsMouse: {
            var topLeft = Qt.point(0-topLeftOffset.x, 0-topLeftOffset.y)
            var topRigh = Qt.point(width-topRightOffset.x, 0-topRightOffset.y)
            var bottomRight = Qt.point(width-bottomRightOffset.x, height-bottomRightOffset.y)
            var bottomLeft = Qt.point(0-bottomLeftOffset.x, height-bottomLeftOffset.y)
            var polygon = [[topLeft.x,topLeft.y],[topRigh.x,topRigh.y],[bottomRight.x,bottomRight.y],[bottomLeft.x,bottomLeft.y]]
            var point = [mouseX, mouseY]
            return inside(point, polygon)
        MouseArea {
            id: mouseArea
            anchors.fill: parent
            hoverEnabled: true
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            onClicked: if (sideButtonID.containsMouse) sideButtonID.clicked()
        Shape {
            antialiasing: true
            vendorExtensionsEnabled: true
            asynchronous: true
            anchors.centerIn: parent
            ShapePath {
                id: shapepathId
                strokeWidth: -1
                fillColor: sideButtonID.pressed ? sideButtonID.colorPressed : (sideButtonID.containsMouse ? sideButtonID.colorSelected : sideButtonID.colorUnselected)
                startX: sideButtonID.topLeft.x; startY: sideButtonID.topLeft.y
                PathLine { x: sideButtonID.topRight.x; y: sideButtonID.topRight.y }
                PathLine { x: sideButtonID.bottomRight.x; y: sideButtonID.bottomRight.y }
                PathLine { x: sideButtonID.bottomLeft.x; y: sideButtonID.bottomLeft.y }
            Component.onCompleted: {
                console.log("TopLeft: "+sideButtonID.topLeft)
                console.log("TopRight: "+sideButtonID.topRight)
                console.log("BottomLeft: "+sideButtonID.bottomLeft)
                console.log("BottomRight: "+sideButtonID.bottomRight)

    The usage of my button:

    SideButtons {
        z: 100
        id: sideButtonID
        width: 130
        height: 248
        anchors.verticalCenterOffset: 9
        anchors.horizontalCenterOffset: -255
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter
        bottomRightOffset: Qt.point(100,0)
        onClicked: {
            print("X: "+mouseX)
            print("Y: "+mouseY)

    Some of the options that I've heard so far but there are not good in my case.

    1. using an image with a transparent background (sure but I'm going to have the same problem)
    2. creating a C++ class which will inherit QAbstractButton (I would prefer to avoid as much as I can usage of C++ in the Ui side)
    3. usage of a Shape in combination with MouseArea (I'm doing this already but I am encountering the bounding box problem.

    Is there any other way which I don't know yet about?

  • Moderators

    usage of a Shape in combination with MouseArea (I'm doing this already but I am encountering the bounding box problem.

    You've got it almost right. In MouseArea's event handler, do not accept events which should be propagated further to your image buttons below. Example code:

    MouseArea {
      propagateComposedEvents: true
      onClicked: {
        if (clickIsInsideShape) {
        } else {
          mouse.accepted = false;

    Docs: https://doc.qt.io/qt-5/qml-qtquick-mousearea.html#clicked-signal

Log in to reply