Details
-
Bug
-
Resolution: Done
-
P1: Critical
-
5.15.1
-
None
-
d92f1dfe05 (qt/qtdeclarative/dev) d92f1dfe05 (qt/tqtc-qtdeclarative/dev) 07e40cf567 (qt/qtdeclarative/6.3) 07e40cf567 (qt/tqtc-qtdeclarative/6.3) 1fe90c8d2e (qt/tqtc-qtdeclarative/5.15) 07e40cf567 (qt/tqtc-qtdeclarative/6.3.1)
Description
first_ok.webm second_ng.webm The PreventStealing of mouseArea does not work properly in Flickable's(ListView) child item.
If you look at the test application, the configuration is as follows.
- ListView
- Custom ScrollBar
The test application is using ScrollBar for ListView. The ScrollBar has been customized and added handles for quick scrolling.
We set the preventStealing property to true on the handle of the ScrollBar.
The screenshot of running the application is as follows.
Test Application codes
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.12 Window { id: root width: 640 height: 480 visible: true title: qsTr("testApplication") property int dynamicModel: 100 Rectangle { anchors.fill: parent Component { id: contactDelegate Rectangle { border.color: "#81e889" width: 640; height: 100 Column { anchors.centerIn: parent Text { text: '<b>Name:</b> ' + index } Text { text: '<b>Number:</b> ' + index } } } } ListView { id: list1 model: root.dynamicModel delegate: contactDelegate highlight: Rectangle { color: "lightsteelblue"; radius: 5 } focus: true width: 640 height: 480 ScrollBar.vertical: ScrollBar { id: control size: 0.3 position: 0.2 active: true orientation: Qt.Vertical contentItem: Rectangle { id: handleBar implicitWidth: 6 implicitHeight: 100 radius: width / 2 color: control.pressed ? "#81e889" : "#c2f4c6" Rectangle { id: handle height: 64 width: 64 anchors.right: handleBar.left anchors.top: handleBar.top visible: true radius: height / 2 color: handleControl.pressed ? "#17a81a" : "#21be2b" Text { anchors.centerIn: parent text: "Drag me" } MouseArea { id: handleControl anchors.fill: parent ////////////////////////////////////////////////////////////////// // set preventStealing to true ////////////////////////////////////////////////////////////////// preventStealing: true drag { target: control.contentItem minimumY: 0 maximumY: control.height - handle.parent.height axis: Drag.YAxis } onPositionChanged: { if (pressed) { control.parent.contentY = control.contentItem.y / control.height * control.parent.contentHeight } } } } } } } } }
Steps to reproduce the issue
1. Run test application
2. Flicking ListView
3. Click and Drag handle when ListView is moving (It's OK!) <---- first_ok.webm
4. Flicking ListView again after ListView is stopping
5. Do step 3 again. (It's a bug) <---- second_ng.webm
- Clicking and dragging the handle does not work. In this situation, the ListView is receiving click and dragging events.
- Expected results : We expect the handle to be clicked and the scrollbar to move through dragging.
- After the movement of the ListView is completely stopped, you can click or drag the handle. But We want it to work(click and drag) when it moves.
The Issue analysis
- The reason why it doesn't work properly on the second try
The event is stolen because receiverKeepsGrab is returned as false in the filterMouseEvent function. keepMouseGrab is returned as false. See below the link.
https://code.woboq.org/qt6/qtdeclarative/src/quick/items/qquickflickable.cpp.html#2582 - Why is keepMouseGrab set to false?
keepMouseGrab is set to false in the mouseReleaseEvent function.
https://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickmousearea.cpp.html#823 - Why it works normally on the first try
In the first attempt, the mouseReleaseEvent function has not yet been executed. So, keepMouseGrab is set by setPreventStealing
https://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickmousearea.cpp.html#572
Our approach is as follows
Fixed KeepMouseGrab not to be set to false when preventStealing is set.
void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) { Q_D(QQuickMouseArea); d->stealMouse = false; d->overThreshold = false; if (!d->enabled && !d->pressed) { QQuickItem::mouseReleaseEvent(event); } else { d->saveEvent(event); setPressed(event->button(), false, event->source()); if (!d->pressed) { // no other buttons are pressed #if QT_CONFIG(quick_draganddrop) if (d->drag) d->drag->setActive(false); #endif // If we don't accept hover, we need to reset containsMouse. if (!acceptHoverEvents()) setHovered(false); QQuickWindow *w = window(); if (w && w->mouseGrabberItem() == this) ungrabMouse(); // this codes !!!! if (!preventStealing()) setKeepMouseGrab(false); } } d->doubleClick = false; }
original codes : https://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickmousearea.cpp.html#823
Please review our approach and hope it gets added upstream.