Home > Mobile >  QML TapHandler on top of child elements (ListView)
QML TapHandler on top of child elements (ListView)

Time:12-22

ListView steals taps from TapHandler regardless of declaration order. That is, the Rectangle, which is declared first, responds to a click, but not the ListView, which also lies below the TapHandler.

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15

Window {
    id: root
    width: 640
    height: 480
    visible: true
    color: tap.pressed ? "#6698D9" : "white"
    title: qsTr("Hello World")

    ColumnLayout {
        anchors.fill: parent

        Rectangle {
            Layout.preferredHeight: text.height
            Layout.fillWidth: true
            color: "transparent"
            border {
                color: "blue"
                width: 3
            }

            Text {
                id: text
                anchors.centerIn: parent
                font.pointSize: 24
                color: "red"
                text: "some text"
            }
        }

        ListView {
            id: seriesView
            Layout.fillWidth: true
            Layout.fillHeight: true
            orientation: ListView.Vertical
            spacing: 5
            clip: true
            model: ["t1", "t2", "t3"]
            delegate: Rectangle {
                width: seriesView.width
                height: childText.height
                color: "transparent"
                border {
                    color: "blue"
                    width: 3
                }

                Text {
                    id: childText
                    anchors.centerIn: parent
                    font.pointSize: 24
                    color: "green"
                    text: modelData
                }
            }
        }
    }

    TapHandler {
        id: tap
        onTapped: console.log("Tapped root")
    }
}

I also do not understand why the tap on the free area is not caught. It's about TapHandler. Not MouseArea.

I would like the TapHandler to catch clicks within the parent element (highlighted in red).

repo link: https://github.com/conelov/SO_qml_TapHandler_question

CodePudding user response:

Right now, because you use Layout.fillHeight: true the ListView extends to the entire bottom. This means the ListView covers both the area covered by your delegates and the blank area.

        ListView {
            id: seriesView
            Layout.fillWidth: true
            Layout.fillHeight: true
        }

As you observed, because ListView gets the mouse events the TapHandler will not get any.

Two things you can do to get the ListView to cede mouse events to the TapHandler. (1) you can declare additional TapHandlers in the ListView delegates. (2) you can reduce blank space of the ListView.

       Item {
            Layout.fillWidth: true
            Layout.fillHeight: true
            ListView {
                id: seriesView
                width: parent.width
                height: Math.min(parent.height, childrenRect.height)
                delegate: Rectangle {
                    // ...
                    TapHandler { }
                }  
            }
      }

Here's a working example that implements the above.

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Page {
    id: root
    
    background: Rectangle {
        color: tap.pressed ? "#6698D9" : "white"
    }
    
    ColumnLayout {
        anchors.fill: parent
        
        Rectangle {
            Layout.preferredHeight: text.height
            Layout.fillWidth: true
            color: "transparent"
            border {
                color: "blue"
                width: 3
            }
            
            Text {
                id: text
                anchors.centerIn: parent
                font.pointSize: 24
                color: "red"
                text: "some text"
            }
        }
        
        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true

            ListView {
                id: seriesView
                width: parent.width
                height: Math.min(parent.height, childrenRect.height)
                orientation: ListView.Vertical
                spacing: 5
                clip: true
                model: ["t1", "t2", "t3"]
                delegate: Rectangle {
                    width: seriesView.width
                    height: childText.height
                    color: "transparent"
                    border {
                        color: "blue"
                        width: 3
                    }
                    
                    Text {
                        id: childText
                        anchors.centerIn: parent
                        font.pointSize: 24
                        color: "green"
                        text: modelData
                    }

                    TapHandler {
                    }
                }
            }
        }
    }
    
    TapHandler {
        id: tap
        onTapped: console.log("Tapped root")
    }
}

You can Try it Online!

With all this effort to make TapHandler work this way, it might be easier to simply use MouseArea { anchors.fill: parent} instead. It wasn't clear why you needed to use TapHandler?

  • Related