QML - Prevent MouseArea's positionChanged signal to propagate in ScrollView - qt

This post is a copy of a message I already sent on the Qt forum but I couldn't get an answer.
You can find the original post here: https://forum.qt.io/topic/113890/prevent-mousearea-s-positionchanged-signal-to-propagate-in-scrollview/4
If the link is dead, everything is copied below:
I am trying to handle a positionChanged signal in a MouseArea (to create kind of a drag&drop effet) that is in a ScrollView.
My problem is that after the mouse travelled a short distance, the parent ScrollView seem's to get the focus (the scrollbar appears) and I stop to receive positionChanged signals.
The objective would be to receive the positionChanged signal (even if the mouse gets out of my MouseArea & over the ScrollView as long as my left mouse button stays pressed) without propagating the signal to the ScrollView.
I have 3 separate examples. This is a simple QML application that should be easy to run.
The two first examples work. The third does not work.
What is "working":
Press the mouse button down on the MouseArea
Move the mouse around without releasing the button
The message that logs coordinates should never stop printing, wherever you are on screen until you release the mouse button.
For the third example, I get logs until the mouse moves too much and all the updates stop.
Only ScrollView (works)
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow{
id: root
visible: true
width: 1200
height: 600
ScrollView {
clip: true
anchors.fill: parent
MouseArea {
width: 300
height: 300
onPositionChanged: {
console.log('Moved', mouseX, mouseY)
}
}
}
}
Only ColumnLayout (works)
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow{
id: root
visible: true
width: 1200
height: 600
ColumnLayout {
MouseArea {
width: 300
height: 300
onPositionChanged: {
console.log('Moved', mouseX, mouseY)
}
}
}
}
ColumnLayout inside a ScrollView (does not work)
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
ApplicationWindow {
id: root
visible: true
width: 1200
height: 600
ScrollView {
clip: true
anchors.fill: parent
// note: It does not work for ColumnLayout, Column, Row or RowLayout. If I use a Item here, it works
ColumnLayout {
MouseArea {
width: 300
height: 300
onPositionChanged: {
console.log('Moved', mouseX, mouseY)
}
}
}
}
}
You can find a video of the behavior here: https://i.imgur.com/rIlWnhu.mp4
I press and release the mouse without moving: I get the pressed & released events correctly
I press and move the mouse: I get the pressed event, the move event, then it stops. No more move or released events.
I press and move the mouse without going too far from the position where I pressed the mouse: It works until I get too far
Note: I don't get any Released or Exited event, but the containsPressed property is correctly updated (ie: when I no longer receive events, its value is false). This is the property that I use to display the "Mouse pressed" text.
Is this something I do wrong with the ScrollView/ColumnLayout combo or is this a Qt bug ?

Add this to your MouseArea in your third example:
preventStealing: true
For more info see:
https://doc.qt.io/qt-5/qml-qtquick-mousearea.html#preventStealing-prop

Related

Setting header on ListView prevents scrolling when the delegate has MouseArea

I have a ListView and its delegate has a MouseArea. If the ListView doesn't have a header, everything works well. However, if I set a header, I can only click on the delegates but can't scroll.
What's is more interesting is that when I set the header's width to half the width of the window, I can scroll normally on the right side of the screen (where there's no header), but can't scroll on the left side under the header.
EDIT: Experimented with this a bit and found another thing. The delegate's height is 80 and if I set the header's height to, say, 30, then I can't swipe when the mouse lands on the top 30 pixels of a delegate as if the header is attached to each item in the list?
[edited code] This the full code that recreates the problem for me (can't scroll on the left side but can on the right). I'm using qt 2.15
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: root
width: 640
height: 600
visible: true
ListView {
id: list
anchors.fill: parent
model: 20
delegate: Rectangle {
signal clicked();
implicitHeight: 70
width: root.width
MouseArea {
anchors.fill: parent
z: 2
onClicked: console.log("clicked");
}
Text {
anchors.centerIn: parent
text: "text"
}
}
// Works fine when I comment this out
headerPositioning: ListView.OverlayHeader
header: Rectangle {
z: 2
implicitHeight: 100
implicitWidth: root.width / 2
color: "blue"
}
}
}
Example (the header is blue):
https://bugreports.qt.io/browse/QTBUG-101727 was written up about this; probably the same as QTBUG-89409. And we fixed it already in 5.15.7. It's also OK in Qt 6.
Note that if you press on the header itself and try to flick, you won't be able to, in any version; that's intentional: https://bugreports.qt.io/browse/QTBUG-74046
The problem turns out to be too specific and can't be recreated so I guess I should close the question.
(SO asks me to wait for 18 hours...)

QML Glow Inside a RowLayout

I am using Qt 5.15 Quick 2 QML to create a row of custom buttons in a window. When I have a standalone custom button things appear to work fine, but when I put them in a RowLayout there appears to be severe clipping and artifacting issues.
A minimum reproducible example might look like:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
RowLayout
{
anchors.fill:parent
anchors.margins: 25
Button
{
text: "Click Me"
Layout.fillWidth: true
}
CustomButton
{
text: "That Boy Don't Glow Right"
}
Button
{
x: 100; y:100
text: "Click Me"
Layout.fillWidth: true
}
}
}
with the custom control
import QtQuick 2.0
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
Button {
id: control
text: "Click Me"
Glow {
anchors.fill: control
radius: 64
spread: 0
samples: 128
color: "red"
source: control
visible: true
}
}
with example output:
One potential fix is to add change the Glow to
Glow {
anchors.fill: control
width: parent.width
height:parent.height
x:control.x
y:control.y
parent: control.parent
...
But this doesn't seem right. First, it's not obvious to me where parent.width and control.x and control.parent are bound from and what happens in single and multiple nesting. If a CustomButton is placed inside another control with id control, would it rebind the property? And it appears if a RowLayout is placed inside a RowLayout, then it would require parent: control.parent.parent. In my actual code there is some non-trivial positioning to allow margins for a drop shadow, too, and the CustomButton is in another container so the actual code that works is: x:control.x + parent.pad/2 and parent:control.parent.parent.parent which is, frankly, ridiculous and assumes that non-standard fields in the parent are always available.
Is there a better way? Was hoping I could keep the button's ability to glow itself.
According to the docs:
"Note: It is not supported to let the effect include itself, for instance by setting source to the effect's parent."
So it's fortunate that you were able to get your example to work at all. One way to avoid using the parent as a source is to point the Glow object at the Button's background object:
Button {
id: control
Glow {
source: control.background
}
}

Top item in a ListView disappears completely even when only partially ouside bounds. How can I fix this?

I'm learning to use Qt and QML. Right now, I'm trying to use a ListView, and I mostly got it to work, except for this one little visual bug.
When I run the code, at first it looks fine. But if I scroll a little bit, the top item disappears completely. It only appears again when scrolling back enough so that it is entirely within bounds. In the mean time, there's only a ugly blank spot in the list. That happens with every item when it goes over the top bound.
I want the items to be partially drawn. The library is clearly capable of doing this, since this problem doesn't happen in the lower bound, but I simply cannot figure out how to do it.
Here's a simplified version of my code:
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: window
width: 360
height: 520
visible: true
title: "Qml.Net"
ListView {
anchors.fill: parent
spacing: 100
model: ["#111111", "#222222", "#333333", "#444444", "#555555", "#666666"]
delegate: Item {
Rectangle {
width: 400
height: 100
color: modelData
Text {
anchors.centerIn: parent
text: modelData
}
}
}
}
}
And here are some pictures of the problem. First image is correct, second image shows the error. Also, notice how the bottom item is correctly drawn.
Correct at first
Wrong after a little bit of scrolling
Qt 5.12
The problem is caused by the "spacing" property that is unnecessary in your case. The solution is to remove that property and rewrite the logic as follows:
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
id: window
width: 360
height: 520
visible: true
title: "Qml.Net"
ListView {
anchors.fill: parent
model: ["#111111", "#222222", "#333333", "#444444", "#555555", "#666666"]
delegate: Rectangle {
width: parent.width
height: 100
color: modelData
Text {
anchors.centerIn: parent
text: modelData
}
}
}
}

Can't get rid of cursor when hiding TextArea in Qt Quick Controls

The issue seems so trivial that I almost believe it's a bug in Qt itself:
import QtQuick.Window 2.2
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Layouts 1.1
Window {
id: window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
color: "white"
Layout.columnSpan: 2
Layout.fillHeight: true
Layout.fillWidth: true
radius: 5
width: 640/2
height: 480/2
TextArea {
id: txtMemo
anchors.fill: parent
anchors.margins: 5
textColor: "black"
wrapMode: TextEdit.Wrap
readOnly: false
}
}
Button {
x: 0
y: 480/2
width: 640/2
height: 480/2
onClicked: {
//Qt.inputMethod.hide()
txtMemo.visible = false
}
}
}
You need to run this on an Android device to see the bug:
Type something in to the text area so the cursor and virtual keyboard appears.
When you click the button, the cursor & keyboard stay on screen. No idea why, perhaps a feature.
Anyways, that's not the main issue. When I uncomment Qt.inputMethod.hide() and trying to reproduce, an interesting thing happens:
if the keyboard is visible, both the cursor and keyboard disappear - awesome, exactly what I want
however if the keyboard isn't visible (closed by the arrow on the bottom during typing) and the cursor is, the cursor won't disappear at all:
(apologies for the picture quality)
So how do I get rid of the cursor? Tested on Qt 5.9.6 on Android (seems unrelated on Android version, happens on the latest version as well).

QML, ScrollView and touch events on desktop

Since few months, directly in ListView item we can use ScrollBar.vertical: ScrollBar {} to provide appropriate scrollbar. Unfortunately it doesn't behave natively for me so I decided to surround every ListView in the project with ScrollView and now it works fine except one thing:
import QtQuick 2.9
import QtQuick.Controls 2.2
//import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Scroll")
ScrollView {
anchors.fill: parent
ListView {
anchors.fill: parent
model: 20
delegate: Text {
text: "Item " + (index + 1)
height: 50
width: parent.width
}
}
}
}
in the example above I cannot use touch event to scroll the view. I'm working on windows 10 and it doesn't matter if I use ScrollView that comes from QtQuick.Controls 1.4 or QtQuick.Controls 2.2 If I press and hold the mouse button, the view won't follow the mouse moves just like it does in case of ListView alone.
Is there any way to restore this behavior for this particular method of handling scrollbars?
use Flickable { } instead of ScrollView.
* EDIT *
Flickable {} allows the user to scroll left and right using touch events such as flicking, dragging, etc.
It is designed for touch screens. You use it the same way as a ScrollView {}, as a container class.
Flickable {
anchors.fill: parent
ListView {
anchors.fill: parent
model: 20
}
}

Resources