Not able to handle child rect mouse events in QML - qt

I have a parent rectangle on top of it there is a child rectangle, both rectangles having mouse events but child rectangle is not taking any mouse event always parent rectangle is handling.
main.qml
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 500
height: 500
title: qsTr("Hello World")
Rectangle{
id: outerrect
color: "green"
anchors.fill: parent
Rectangle{
id: innerrect
width: 100
height: 100
color: "lightblue"
anchors.centerIn: parent
MouseArea{
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("child")
}
}
}
MouseArea{
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("parent")
}
}
}
}
Issue:
Not able to handle child rectangle mouse events

See the example and explanation for the property propagateComposedEvents of MouseArea QML Type
So, if you want to handle the click only by the child rectangle you can change the order of the MouseArea block in the parent and the child Rectangle block. It changes the order of handling events by blocks.
To activate both handlers the top object should have propagateComposedEvents: true property and also mouse.accepted = false should be set in the onClicked handler, for example:
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
hoverEnabled: true
onClicked: {
mouse.accepted = false
console.log("parent")
}
}

Related

QML: Button inside MouseArea does not pass mouse hover events

There is a button inside MouseArea:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Rectangle {
id: background
anchors.fill: parent
color: mouseArea.containsMouse ? "red" : "green"
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onEntered: console.log("onEntered")
onExited: console.log("onExited")
//onPositionChanged: console.log("position", mouse.x, mouse.y)
Button {
id: button
anchors.centerIn: parent
width: 200
height: 100
}
}
}
In Qt5, if you hover the mouse over the button, the containsMouse property will remain true and the onExited and onEntered handlers will not be called.
If you switch to Qt6, then when you move the mouse over the button, the onExited and onEntered handlers are called and containsMouse becomes false.
I need hover events behavior in Qt6 the same as in Qt5. Is it possible?

QML: Attach scrollbar to ListView

I'm having an issue with ListView. ListView is too long and part of it appears outside of the window but I can't attach a scrollbar. I tried many different combination. I think that problem lies in height parameter but if remove it ListView displays only first entry.
Column{
anchors.fill: parent
Row{
id: buttonsRow
Button{
text: "Open dump file"
onClicked: fileDialog.visible = true
}
Button{
text: "Copy raw data to clipboard"
}
}
ListView{
id: listView
anchors.top: buttonsRow.bottom
height: contentHeight
//clip: true
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.StopAtBounds
interactive: true
model: ListModel{
id: listModel
}
delegate: MDelegate{}
}
}
Is there any way to make it scrollable?
I don't see, in the code you posted, where you've attached a scrollbar at all. You need to actually include a ScrollBar component in your ListView, like this:
ListView {
id: listView
ScrollBar.vertical: ScrollBar {
active: true
}
}
See "Attaching ScrollBar to a Flickable" at https://doc.qt.io/qt-5/qml-qtquick-controls2-scrollbar.html
Setting height to contentHeight is probably the issue. That would make the ListView as high as all of its item's heights combined. The scrollbar only works when the height of the view is less than the height of its contents.
Here's an approach that uses layouts instead:
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
ApplicationWindow {
width: 400
height: 300
visible: true
ColumnLayout {
anchors.fill: parent
RowLayout {
id: buttonsRow
Button {
text: "Open dump file"
}
Button {
text: "Copy raw data to clipboard"
}
}
ListView {
id: listView
flickableDirection: Flickable.VerticalFlick
boundsBehavior: Flickable.StopAtBounds
model: 100
clip: true
delegate: ItemDelegate {
text: modelData
}
Layout.fillWidth: true
Layout.fillHeight: true
ScrollBar.vertical: ScrollBar {}
}
}
}
ScrollBar.vertical:ScrollBar{
id: listView
anchors.right: parent.right
visible: listView.contentHeight > listView.height ? true : false
}

QML ListView focus on first item except header delegate

I use QML ScrollView and ListView. ListView consists of headerDelegate and delegate:
ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
width: parent.width
height: parent.height
ListView{
id: listView
width: parent.width
height: parent.height
spacing: 10
header: headerDelegate
anchors.fill: parent
property bool isFolded: false
model: MyModel
delegate: mainDelegate
}
}
Everytime ListView is scrolled to the top the first mainDelegate is shown instead of headerDelegate which remains hidden. How can I force the scroll to correctly show headerDelegate?
The problem here is that ScrollView expects child's contentY to be equal 0 but ListView positions the first item of the model at contentY = 0 and places header item before it. If ListView.header is 50px high then it is positioned at ListView.contentY = -50.
The solution that worked for me was to emit ListView.contentYChanged() signal. It causes ScrollView to update. Let me know if this solves your issue.
ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
width: parent.width
height: parent.height
ListView{
id: listView
width: parent.width
height: parent.height
spacing: 10
header: headerDelegate
anchors.fill: parent
property bool isFolded: false
model: listModel
delegate: mainDelegate
onContentYChanged: console.log(contentY)
Component.onCompleted: {
contentYChanged()
}
}
}
I've changed listView.contentY manually. After finally loading header (I got event) I've set listView.contentY = 0 - headerDelegateView.height and it works. #Filip Hazubski thank you for good way of solution!
example code
Rectangle
{
id: headerDelegateView
WebEngineView{
...
onLoadingChanged: {
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
... // here I calculate web page height by java script and set to headerDelegateView.height
listView.contentY = 0 - headerDelegateView.height
}
ScrollView {
...
ListView{
id: listView
...
}
}
}

MouseArea inside Flickable is preventing it from flicking

I'm implementing gesture catcher (swipe left/right) with MouseArea. It should work inside Flickable with vertical flickableDirection. Also it should propagate mouse events to other elements beneath it in visual stack order. The problem is that child mouseArea with propagateComposedEvents set to true is blocking any parent's flicks before exact one click is made. After first click is made it's working correctly. Here is simplified code that is showing this.
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: __root
visible: true
width: 460; height: 640
Flickable {
id: mainFlickable
width: parent.width
height: parent.height
contentHeight: column.height
flickableDirection: Flickable.VerticalFlick
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
z: 1
}
Column {
id: column
width: parent.width
Repeater {
model: 5
Rectangle {
width: __root.width
height: 200
color: "yellow"
border.width: 2
MouseArea {
anchors.fill: parent
onClicked: {
console.log("clicked")
}
}
}
} //repeater
} //column
} //flickable
} //window
I spent quite some time trying to fix this and will appreciate any help. Thanks in advance!
I found that following signal handler in MouseArea is a workaround for this and don't break my code:
onReleased: {
if (!propagateComposedEvents) {
propagateComposedEvents = true
}
}
propagateComposedEvents should be set to false on the declaration (or ommited).
Thank you all for the efforts!
I found little workaround for this. Hope it will fit your needs (at least until better solution will be provided).
Here is your updated code:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: __root
visible: true
width: 460; height: 640
Flickable {
id: mainFlickable
width: parent.width
height: parent.height
contentHeight: column.height
flickableDirection: Flickable.VerticalFlick
onDragStarted: ma.enabled = false
onDragEnded: ma.enabled = true
MouseArea {
id: ma
anchors.fill: parent
enabled: false
propagateComposedEvents: true
z: 100
onClicked: {
print("CLICKED ON UPPER")
mouse.accepted = false
}
}
Column {
id: column
width: parent.width
Repeater {
model: 5
Rectangle {
width: __root.width
height: 200
color: "yellow"
border.width: 2
MouseArea {
anchors.fill: parent
onClicked: console.log("clicked on child")
}
}
} //repeater
} //column
} //flickable
} //window

MouseArea event is propagated to its parent sibling. Why?

Here's a code snippet with current form of the code
Rectangle
{
id: menu
GridLayout
{
id: layout
columns: 4
rows: 3
Repeater
{
model: ListModel {}
ToolButton {}
}
Rectangle
{
x: -3
y: -33
width: menu.width - 2
height: menu.height + 33
border.color: "red"
border.width: 3
color: "blue"
MouseArea
{
x: mapToItem(menu, -5, -35).x
y: mapToItem(menu, -5, -35).y
width: menu.width
height: menu.height + 35
hoverEnabled: true
preventStealing: true
onEntered:console.log("onEntered")
onExited:console.log("onExited menu mous area")
}
}
}
}
The MouseArea hover event is propagated down to the ToolButtons in the layout. I don't get why. Hence, the onEntered and onExited events do not work as expected, because onExited happen inside the MouseArea when the ToolButtons are 'hovered' and tooltips are shown. In the end I need the MouseArea to be a bit wider and longer than its parent Rectangle so that once onExited is emitted the menu gets invisible. After the test with Rectangle is successfull it will make sense to make C++ type Polygon.
In your example, onExited must emits when entering ToolButton. According to MouseArea.exited():
Rectangle {
width: 400; height: 400
MouseArea {
id: mouseArea1
anchors.fill: parent
hoverEnabled: true
}
MouseArea {
id: mouseArea2
width: 100; height: 100
anchors.centerIn: parent
hoverEnabled: true
}
}
Moving the mouse into mouseArea2 from mouseArea1 will cause mouseArea1 to emit the exited signal.
If you do not want the exited signal to be emitted,
If instead you give the two MouseAreas a parent-child relationship, moving the mouse into mouseArea2 from mouseArea1 will not cause mouseArea1 to emit exited. Instead, they will both be considered to be simultaneously hovered.
That is, place ToolButton (and all related components) within the MouseArea. For example,
Rectangle {
id: menu
Rectangle {
//some properties
MouseArea {
hoverEnabled: true
//some properties
onEntered:console.log("onEntered")
onExited:console.log("onExited menu mous area")
GridLayout {
id: layout
Repeater {
ToolButton {}
}
}
}
}
}

Resources