Unity 8
DraggingArea.qml
1 /*
2  * Copyright (C) 2013 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 
20 MouseArea {
21  id: draggingArea
22 
23  property int orientation: Qt.Vertical
24  property bool dragging
25  property real dragVelocity: 0
26  property real dragValue: (orientation == Qt.Vertical ? (mouseY - __pressedPosition.y)
27  : (mouseX - __pressedPosition.x))
28  property real lateralPosition: orientation == Qt.Horizontal ? MathUtils.clamp(mouseY, 0, height) : MathUtils.clamp(mouseX, 0, width)
29  property point __pressedPosition: Qt.point(0, 0)
30  property var __dragEvents: []
31  property bool clickValidated: true
32  property bool zeroVelocityCounts: false
33 
34  // Can be replaced with a fake implementation during tests
35  // property var __getCurrentTimeMs: function () { return new Date().getTime() }
36  property var __dateTime: new function() {
37  this.getCurrentTimeMs = function() {return new Date().getTime()}
38  }
39 
40 
41  signal dragStart
42  signal dragEnd
43 
44  onDragValueChanged: {
45  if (dragValue != 0 && pressed) {
46  dragging = true
47  }
48  }
49 
50  onDraggingChanged: {
51  if (dragging) {
52  dragStart()
53  }
54  else {
55  dragEnd()
56  }
57  }
58 
59  function updateSpeed() {
60  var totalSpeed = 0
61  for (var i=0; i<__dragEvents.length; i++) {
62  totalSpeed += __dragEvents[i][3]
63  }
64 
65  if (zeroVelocityCounts || Math.abs(totalSpeed) > 0.001) {
66  dragVelocity = totalSpeed / __dragEvents.length * 1000
67  }
68  }
69 
70  function cullOldDragEvents(currentTime) {
71  // cull events older than 50 ms but always keep the latest 2 events
72  for (var numberOfCulledEvents=0; numberOfCulledEvents<__dragEvents.length-2; numberOfCulledEvents++) {
73  // __dragEvents[numberOfCulledEvents][0] is the dragTime
74  if (currentTime - __dragEvents[numberOfCulledEvents][0] <= 50) break
75  }
76 
77  __dragEvents.splice(0, numberOfCulledEvents)
78  }
79 
80  function getEventSpeed(currentTime, event) {
81  if (__dragEvents.length != 0) {
82  var lastDrag = __dragEvents[__dragEvents.length-1]
83  var duration = Math.max(1, currentTime - lastDrag[0])
84  if (orientation == Qt.Vertical) {
85  return (event.y - lastDrag[2]) / duration
86  } else {
87  return (event.x - lastDrag[1]) / duration
88  }
89  } else {
90  return 0
91  }
92  }
93 
94  function pushDragEvent(event) {
95  var currentTime = __dateTime.getCurrentTimeMs()
96  __dragEvents.push([currentTime, event.x, event.y, getEventSpeed(currentTime, event)])
97  cullOldDragEvents(currentTime)
98  updateSpeed()
99  }
100 
101  onPositionChanged: {
102  if (dragging) {
103  pushDragEvent(mouse)
104  }
105  if (!draggingArea.containsMouse)
106  clickValidated = false
107  }
108 
109  onPressed: {
110  __pressedPosition = Qt.point(mouse.x, mouse.y)
111  __dragEvents = []
112  pushDragEvent(mouse)
113  clickValidated = true
114  }
115 
116  onReleased: {
117  dragging = false
118  __pressedPosition = Qt.point(mouse.x, mouse.y)
119  }
120 
121  onCanceled: {
122  dragging = false
123  }
124 }