[Interest] Mouse event propagation in Qt Quick
J-P Nurmi
jpnurmi at qt.io
Sun Oct 16 16:06:01 CEST 2016
Hey Mitch,
> - In example #2, why is Flickable happy to steal events that it doesn't do anything with? Shouldn't it see that it wasn't a "flick" and ignore the event, so that it goes to the next highest item in the stacking order (the mouse area)?
An interactive Flickable always accepts mouse press events, because it needs to become the "mouse grabber item" ie. the item that receives the consequent mouse move events. This way Flickable can detect drags and flicks.
> - Why aren't the scroll bars blocked by the mouse area in example #3?
The MouseArea is under the ScrollBar, because its parent is under the ScrollBar. The MouseArea is a child of Flickable::contentItem, whereas ScrollBars are children of the Flickable they are attached to. ScrollBar and Flickable::contentItem are siblings, ScrollBar being higher in the stacking order.
> - Why does example #3 work if I remove "preventStealing: true"?
What do you mean? That's the exact use case "preventStealing" is meant for. :) When the MouseArea's preventStealing is true, Flickable honors it and won't be able to flick or drag since it's not allowed to steal events from the MouseArea. When preventStealing is false, Flickable's childMouseEventFilter() steals the press from the MouseArea when it detects a flick or drag.
--
J-P Nurmi
________________________________________
From: Interest <interest-bounces+jpnurmi=qt.io at qt-project.org> on behalf of Mitch Curtis <mitch.curtis at qt.io>
Sent: Sunday, October 16, 2016 3:42:35 PM
To: Qt Project
Subject: [Interest] Mouse event propagation in Qt Quick
Hi.
In the following example (#1), I want both the MouseArea to be clickable and the scroll bars to be draggable:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: root
Flickable {
anchors.fill: parent
contentWidth: rect.width
contentHeight: rect.height
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar {
id: verticalScrollBar
Binding {
target: verticalScrollBar
property: "active"
value: verticalScrollBar.hovered
}
}
Rectangle {
id: rect
width: 640
height: 1000
gradient: Gradient {
GradientStop {
position: 0
color: "#e03389"
}
GradientStop {
position: 1
color: "#20ae24"
}
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
}
Rectangle {
id: mouseAreaRect
anchors.fill: parent
color: "transparent"
border.color: mouseArea.pressed ? "red" : "darkorange"
}
}
The mouse area can be clicked, but the scroll bars can't be dragged.
If I move the mouse area below the flickable, the opposite problem occurs: the scroll bars can be dragged, but the mouse area can't be clicked. Example #2:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: root
MouseArea {
id: mouseArea
anchors.fill: parent
}
Flickable {
anchors.fill: parent
contentWidth: rect.width
contentHeight: rect.height
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar {
id: verticalScrollBar
Binding {
target: verticalScrollBar
property: "active"
value: verticalScrollBar.hovered
}
}
Rectangle {
id: rect
width: 640
height: 1000
gradient: Gradient {
GradientStop {
position: 0
color: "#e03389"
}
GradientStop {
position: 1
color: "#20ae24"
}
}
}
}
Rectangle {
id: mouseAreaRect
anchors.fill: parent
color: "transparent"
border.color: mouseArea.pressed ? "red" : "darkorange"
}
}
I remembered that MouseArea has a preventStealing property. Its documentation says:
This property holds whether the mouse events may be stolen from this MouseArea.
If a MouseArea is placed within an item that filters child mouse events, such as Flickable, the mouse events may be stolen from the MouseArea if a gesture is recognized by the parent item, e.g. a flick gesture. If preventStealing is set to true, no item will steal the mouse events.
I only want the gradient rectangle to be the child of the flickable, but I was curious if it would work, so I moved the mouse area in there anyway. Example #3:
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: root
Flickable {
anchors.fill: parent
contentWidth: rect.width
contentHeight: rect.height
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar {
id: verticalScrollBar
Binding {
target: verticalScrollBar
property: "active"
value: verticalScrollBar.hovered
}
}
Rectangle {
id: rect
width: 640
height: 1000
gradient: Gradient {
GradientStop {
position: 0
color: "#e03389"
}
GradientStop {
position: 1
color: "#20ae24"
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
preventStealing: true
}
}
Rectangle {
id: mouseAreaRect
anchors.fill: parent
color: "transparent"
border.color: mouseArea.pressed ? "red" : "darkorange"
}
}
To my surprise, it worked. I thought that the MouseArea would block the scroll bars. From some debugging, I can see that the scroll bar is constructed before the mouse area, so I would have thought that it would be below it in terms of stacking order.
I then wondered if I could get away with not even setting preventStealing to true, and it turns out I could.
I have some questions about all of this:
- In example #2, why is Flickable happy to steal events that it doesn't do anything with? Shouldn't it see that it wasn't a "flick" and ignore the event, so that it goes to the next highest item in the stacking order (the mouse area)?
For instance, in the following example, there are two sibling mouse areas. The lower mouse area gets the press event, because the one above it wasn't interested in it:
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
id: root
MouseArea {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: "transparent"
border.color: parent.pressed ? "black" : "green"
border.width: 5
}
}
MouseArea {
anchors.fill: parent
onPressed: mouse.accepted = false
Rectangle {
anchors.fill: parent
anchors.margins: 10
color: "transparent"
border.color: parent.pressed ? "red" : "darkorange"
border.width: 5
}
}
}
This is the behaviour I'd expect.
- Why aren't the scroll bars blocked by the mouse area in example #3?
- Why does example #3 work if I remove "preventStealing: true"?
Cheers.
_______________________________________________
Interest mailing list
Interest at qt-project.org
http://lists.qt-project.org/mailman/listinfo/interest
More information about the Interest
mailing list