How make QML ListView not flickable? - qt

I was wondering if there's any way for ListView to behave like a desktop control and not react with scrolling to mouse dragging?
I know about the interactive property, but I still want the ListView to react to clicks, mouse wheel, arrow keys, and have a ScrollBar.

For starters, setting interactive to false will pretty much immobilize the view.
There is a keyNavigationEnabled property which doesn't seem to work at this moment(this critical bug).
So will need to do a little extra work to get it to work as you want:
MouseArea {
anchors.fill: ll
onWheel: ll.flick(0, wheel.angleDelta.y * 5)
}
ListView {
id: ll
model: 50
width: 50
height: 200
spacing: 5
focus: true
interactive: false
boundsBehavior: Flickable.StopAtBounds
Keys.onPressed: {
if (event.key === Qt.Key_Up) flick(0, 500)
else if (event.key === Qt.Key_Down) flick(0, -500)
}
delegate: Rectangle {
width: 50
height: 50
color: "red"
MouseArea {
anchors.fill: parent
onClicked: console.log("clicked")
}
}
}
Interactivity is disabled, key navigation is implemented manually, and a background MouseArea is used to capture wheel events. Note that you don't have to do anything special to enable clicking on items for a non-interactive view, it works regardless of the view is interactive or not.

Related

How can I disable the mouse wheel for a TextArea without disabling the entire control?

I'm using a TextArea to display multi-line text with embedded <IMG ...> tags in the delegate for a ListView. I have it set to read-only (and not disabled) because I need hyperlinks in the text to be clickable so I need to make use of its onLinkActivated event handler. This is normally something that would call for a Label (which does not handle mouse wheel events), but a Label does not render line-breaks correctly when the text includes <IMG ...> tags in the HTML.
The problem I'm having is that a TextArea handles mouse wheel events even when it is read-only, so if the cursor happens to be over one of the visible TextArea controls, the ListView will not respond to mouse wheel events (and so it does not scroll). In other words, the TextArea is capturing the mouse wheel events and I want it to not do this.
I see in the docs that controls have a wheelEnabled: property, but TextArea does not seem to support this.
Update: here is a minimum code sample that demonstrates the problem:
import QtQuick.Controls 1.4 as Controls
Rectangle {
id: test
color: "white"
width: 300
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
Controls.ScrollView {
id: _scrollview
anchors.fill: parent
ListView {
anchors.fill: parent
model: 100
delegate: Rectangle {
id: tableRow
width: test.width
height: 50
color: "yellow"
TextArea {
width: test.width / 2
height: tableRow.height
readOnly: true
text: "Row # " + index
}
}
}
}
}
If you hold the mouse cursor over the right side of this listview (i.e. not over the TextArea control in the row), the mouse wheel works as expected. But if you hold the mouse cursor over the TextArea in any of the rows, the ListView will not scroll with the mouse wheel (because the readOnly TextView is capturing the events).
This is actually pretty easy, too bad I wasted the bounty. All this requires is a MouseArea positioned over the TextArea like so:
MouseArea {
anchors.fill: txtTester
onPressed: {
mouse.accepted = false
}
onReleased: {
mouse.accepted = false
}
property int scrollValue: 15
onWheel: {
if (wheel.angleDelta.y < 0) {
//make sure not to scroll too far
if (!_scrollview.flickableItem.atYEnd)
_scrollview.flickableItem.contentY += scrollValue
}
else {
//make sure not to scroll too far
if (!_scrollview.flickableItem.atYBeginning)
_scrollview.flickableItem.contentY -= scrollValue
}
}
}
This ignores press and release events so clicking on hyperlinks in the TextArea still works, but it intercepts mouse wheel events and applies them to moving the ScrollView as if the TextArea were not there.
Try this out:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Controls 1.3
Window {
visible: true
width: 400
height: 200
TextArea {
id: text
anchors.fill: parent
text: "Current\ntext\n\\to\nmove\ndown\ndown\ndown
\ndown\ndown\ndown\ndown\ndown\ndown\ndown"
flickableItem.interactive: false
}
}
TextArea has flickableItem.enabled property. Since you're stuck with !t 5.6 this should work for you.
EDIT: changed to flickableItem.interactive instead.

QML - How to drag a ToolButton?

The QT documentation has this tutorial.
I initially followed it exactly, and it works. I then made two modifications:
I replaced the ListView with a GridView (that works without #2).
I attempted to add a ToolButton to my delegate inside the Rectangle "content" like so:
Rectangle {
id: content
ToolButton {
id: toolButton
icon.color = "transparent"
icon.source = "image://Loader/iconName"
}
Drag.active: dragArea.held
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
}
This does not work, the ToolButton appears to be processing the mouse movements and not propagating the messages (I can click the button, but I can not drag it)? This is actually somewhat expected to be honest.
So that said, does anyone have a good way of dragging ToolButtons around? Or is it just accepted that you can't do that? I have tried various combinations of Rectangles and MouseAreas but I can't seem to do one without breaking the other (ie either the drag fails or the button fails).
You can move the MouseArea as a child of the ToolButton to manage the drag with pressAndHold, and propagate the click to keep the button behavior:
Rectangle {
id: content
ToolButton {
id: toolButton
// bind the visual state of the button to the MouseArea
background: Rectangle {
color: marea.pressed
? Qt.darker("blue")
: marea.containsMouse
? Qt.lighter("blue")
: "blue" // use your desired colors
}
MouseArea {
id: marea
property bool held: false
drag.target: held ? content : undefined
drag.axis: Drag.YAxis
anchors.fill: parent
hoverEnabled: true
onPressAndHold: held = true
onReleased: held = false
onClicked: toolButton.clicked() // propagate clicked event to the ToolButton
}
}
// ...
}

How can I get the drop effect of a DragArea to animate towards the DropArea that received it?

Hope this makes some sense as a question. In my app, I have a DragArea defined which I use to start dragging things over top of various Rectangles that each contain a DropArea. Everything is working fine in my code except for a cosmetic effect that I would like to change.
In QML, when you start dragging from a DragArea and eventually drop, the animation effect is such that the thing you're dragging animates (while fading out) back to the spot from which you started dragging. This happens even when you drop over a DropArea that successfully captures the drop.
What I would like to do is have the drop effect animate towards the DropArea that received the drop - so that it appears I am dragging-and-dropping things into the Rectangle. Is there any way to do this?
I'm guessing that this in some way involves the .source and .target properties of these areas, but no luck so far in having any effect on where the drop animation goes.
By default, QML will give you no cosmetic behavior for drag and drop whatsoever. The drag target will begin at the drag start location, and will end wherever it is dropped, regardless of whether the drag is accepted or not.
Thus I assume the behavior you describe is implemented in your user code, which you have not disclosed. Regardless, what you want to do is quite easy, it involves tracking the position the drag originates at and it ends at, so you can use the two coordinates to animate the position.
In the following example the red rectangle can be dragged, and if dropped outside of a drop area it will animate from its current to its initial position, whereas if dropped in the yellow rectangle, it will animate from its initial to its drop position.
Window {
width: 600
height: 600
visible: true
Rectangle {
width: 200
height: 200
color: "yellow"
DropArea {
anchors.fill: parent
onEntered: drag.source.accepted = true
onExited: drag.source.accepted = false
}
}
Rectangle {
id: rect
width: 50
height: 50
color: "red"
x: parent.width * 0.5
y: parent.height * 0.5
Drag.active: mouseArea.drag.active
property point begin
property point end
property bool accepted : false
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: parent
onPressed: rect.begin = Qt.point(rect.x, rect.y)
onReleased: {
rect.end = Qt.point(rect.x, rect.y)
aX.from = rect.accepted ? rect.begin.x : rect.end.x
aX.to = rect.accepted ? rect.end.x : rect.begin.x
aY.from = rect.accepted ? rect.begin.y : rect.end.y
aY.to = rect.accepted ? rect.end.y : rect.begin.y
anim.start()
}
ParallelAnimation {
id: anim
NumberAnimation { id: aX; target: rect; property: "x"; duration: 200 }
NumberAnimation { id: aY; target: rect; property: "y"; duration: 200 }
}
}
}
}

Get MouseEvents while MousePressed

I have several QML Items that all have Mouse Areas.
What I want to achieve is:
Click one of the items and start tracking the mouse
Add every other Item that the mouse enters into a list
End the tracking once the mouse is released
Sample Code:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
visible: true
width:500; height: 200;
Rectangle{
anchors.left: parent.left
color: 'red'
width: 200; height: 200;
MouseArea {
anchors.fill: parent
hoverEnabled: true
onReleased: console.log('onReleased red')
onEntered: console.log('onEntered red')
onPressed: {
console.log('onPressed red')
mouse.accepted = false
}
}
}
Rectangle{
anchors.right: parent.right
color: 'blue'
width: 200; height: 200;
MouseArea {
anchors.fill: parent
hoverEnabled: true
onReleased: console.log('onReleased blue')
onEntered: console.log('onEntered blue')
onPressed: console.log('onPressed blue')
}
}
}
Expected behaviour:
Click one Rectangle
Get on entered event if I enter the other element
Get the Released event
The sample code has both Version I tried, with and without accepting the mousePressed event.
What happens is:
If I press the mouse over one rectangle I do not get the onEnter event for all my other rectangles.
If I do not accept the onPressed event, I get the onEnter Events but not the onReleased event.
Note:
I already found this Answer which uses a DropArea as workaround which is not what I want to use if there is any other solution.
Even though the Example may look like Drag&Drop it is not what I want.
Please see the "What I want to achieve" at the top of this Question.
You will not be able to achieve what you want just using standart MouseArea components. Standart QML components are kinda limited in their functionality.
What you have to do is to create your own MouseArea component through QML extension.
In our project we also encountered lots of problems with mouse handling, so how we managed to do it was:
Subclassed QQuickItem, and inside of this class we just tracked the mouse movement, and mouse buttons states. One important thing wass to install EventFilters defined by this class.
In QML, created a Simple Component that Checks if mouse is inside of currrent component.
If you need, i could post also a code here so you have an idea.
The implementation is not the prettiest, but it works

Gridview and MouseArea inside conflict

I have code like this:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
visible: true
width: 500
height: 500
GridView {
id: grid
anchors.fill: parent
cellWidth: 30
cellHeight: 30
model: 120
delegate: Rectangle {
width: grid.cellWidth
height: grid.cellHeight
color: "grey"
}
MouseArea {
anchors.fill: parent
onPressed: console.info(">> PRESSED triggered")
onMouseXChanged: console.info(">> " + mouseX)
onReleased: console.info(">> RELEASED triggered")
preventStealing: true
propagateComposedEvents: true
}
}
}
When I hold mouse button down and move it strictly along X axis, all the signals in MouseArea trigger. But when the mouse is also moved up or down, along Y axis, the onMouseXChanged and onReleased signals do not trigger. It seems that GridView intercepts MouseArea's signals, stealing them from the MouseArea. How can I make them work together: GridView with MouseArea inside?
Set preventStealing to true in your MouseArea.
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.

Resources