1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
/*
* This file is part of unity-2d
*
* Copyright 2010-2011 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 1.0
/* AutoScrollingListView
ListView that has two mouse sensitive areas, one at the top and one at the bottom.
While the cursors is in these areas the list automatically scrolls in the direction
where the area is until the cursor exits the area or the scrolling reaches the edge.
The property 'autoScrollSize' controls how tall are the two sensitive areas.
The speed of the scrolling is controlled by 'autoScrollVelocity'.
You can check if we are currently autoscrolling using the 'autoScrolling' property.
*/
ListView {
id: list
property int autoScrollSize
property real autoScrollVelocity
property bool autoScrolling: scrollUp.running || scrollDown.running
MouseArea {
id: scrollZoneTop
width: parent.width
height: autoScrollSize
anchors.top: parent.top
hoverEnabled: true
z: overlayZ
}
MouseArea {
id: scrollZoneBottom
width: parent.width
height: autoScrollSize
anchors.bottom: parent.bottom
hoverEnabled: true
z: overlayZ
}
SmoothedAnimation {
id: scrollUp
target: list
property: "contentY"
to: 0
velocity: autoScrollVelocity
running: scrollZoneTop.containsMouse || draggingOnScrollZoneTop
}
SmoothedAnimation {
id: scrollDown
target: list
property: "contentY"
to: contentHeight - height
velocity: autoScrollVelocity
running: (scrollZoneBottom.containsMouse && contentHeight > height)
|| draggingOnScrollZoneBottom
}
function stopAutoScrolling() {
scrollUp.stop()
scrollDown.stop()
}
/* The code below this comment is only needed as a workaround for a strange behavior
(or bug) in QML. Essentially MouseEvents have an accepted property, but in most cases
it doesn't matter what you set it to: the event is always accepted and not propagated
further. This prevents mouse activity on the two MouseAreas to flow down to the list
items. Therefore we need to forward the events we need (entered, exited, clicked).
The QT bug reference is:
http://bugreports.qt.nokia.com/browse/QTBUG-13007?focusedCommentId=137123
*/
property variant itemBelow: null
Connections {
target: scrollZoneTop
onEntered: updateItemBelow(scrollZoneTop)
onExited: { if (itemBelow) itemBelow.exited(); itemBelow = null }
onPositionChanged: updateItemBelow(scrollZoneTop)
onClicked: forwardClick(scrollZoneTop, mouse)
}
Connections {
target: scrollZoneBottom
onEntered: updateItemBelow(scrollZoneBottom)
onExited: { if (itemBelow) itemBelow.exited(); itemBelow = null }
onPositionChanged: updateItemBelow(scrollZoneBottom)
onClicked: forwardClick(scrollZoneBottom, mouse)
}
onContentYChanged: updateItemBelow((scrollZoneBottom.containsMouse) ? scrollZoneBottom :
(scrollZoneTop.containsMouse) ? scrollZoneTop : null)
function updateItemBelow(zone) {
if (zone == null) return
var point = zone.mapToItem(list.contentItem, zone.mouseX, zone.mouseY)
var item = list.contentItem.childAt(point.x, point.y)
/* Ignore header, footer and any item that doesn't have the signals
we need to forward */
if (item && (typeof(item.entered) != "function" ||
typeof(item.exited) != "function")) item = null;
if (item == null) {
if (itemBelow != null) {
itemBelow.exited()
itemBelow = null
}
} else {
if (itemBelow != item && itemBelow != null) itemBelow.exited()
item.entered()
itemBelow = item
}
}
/* FIXME: We forward the click but the coordinates will be wrong since they are expressed in the
coordinate system of the MouseArea where it happened. However they are readonly properties in
the MouseEvent so we can't update them with the translated values.
It should be ok for now since we don't use them for now.
*/
function forwardClick(zone, mouse) {
var point = zone.mapToItem(list.contentItem, zone.mouseX, zone.mouseY)
var item = list.contentItem.childAt(point.x, point.y)
if (item && typeof(item.clicked) == "function") item.clicked(mouse)
}
/* If drag and drop reordering is enabled for this list, this will not
be null. Normally we could keep autoscrolling and drag and drop reordering
entirely separated, but due to the QT issue explained above we can't let
the mouse events "bubble down" from the d'n'd MouseArea to the autoscroll
MouseAreas, so we need a workaround like this one. */
property variant dragAndDrop: null
property bool draggingOnScrollZoneTop: dragAndDrop != null && dragAndDrop.draggedTileId != "" &&
dragAndDrop.mouseY >= scrollZoneTop.y &&
dragAndDrop.mouseY <= scrollZoneTop.y + autoScrollSize
property bool draggingOnScrollZoneBottom: dragAndDrop != null && dragAndDrop.draggedTileId != "" &&
dragAndDrop.mouseY >= scrollZoneBottom.y &&
dragAndDrop.mouseY <= scrollZoneBottom.y + autoScrollSize
}
|