@@ -0,0 +1,262 @@
1+import QtQuick 2.4
2+import Ubuntu.Components 1.3
3+import Ubuntu.Gestures 0.1
4+import "../../Components"
5+
6+Item {
7+ id: root
8+
9+ property bool closeable: true
10+ readonly property real minSpeedToClose: units.gu(40)
11+ property bool zeroVelocityCounts: false
12+
13+ readonly property alias containsMouse: mouseArea.containsMouse
14+ readonly property alias distance: d.distance
15+
16+ signal clicked()
17+ signal close()
18+
19+ QtObject {
20+ id: d
21+ property real distance: 0
22+ property bool moving: false
23+ property var dragEvents: []
24+ property real dragVelocity: 0
25+
26+ // Can be replaced with a fake implementation during tests
27+ // property var __getCurrentTimeMs: function () { return new Date().getTime() }
28+ property var __dateTime: new function() {
29+ this.getCurrentTimeMs = function() {return new Date().getTime()}
30+ }
31+
32+ function pushDragEvent(event) {
33+ var currentTime = __dateTime.getCurrentTimeMs()
34+ dragEvents.push([currentTime, event.x, event.y, getEventSpeed(currentTime, event)])
35+ cullOldDragEvents(currentTime)
36+ updateSpeed()
37+ }
38+
39+ function cullOldDragEvents(currentTime) {
40+ // cull events older than 50 ms but always keep the latest 2 events
41+ for (var numberOfCulledEvents = 0; numberOfCulledEvents < dragEvents.length-2; numberOfCulledEvents++) {
42+ // dragEvents[numberOfCulledEvents][0] is the dragTime
43+ if (currentTime - dragEvents[numberOfCulledEvents][0] <= 50) break
44+ }
45+
46+ dragEvents.splice(0, numberOfCulledEvents)
47+ }
48+
49+ function updateSpeed() {
50+ var totalSpeed = 0
51+ for (var i = 0; i < dragEvents.length; i++) {
52+ totalSpeed += dragEvents[i][3]
53+ }
54+ print("total speed", totalSpeed)
55+
56+ if (zeroVelocityCounts || Math.abs(totalSpeed) > 0.001) {
57+ dragVelocity = totalSpeed / dragEvents.length * 1000
58+ }
59+ }
60+
61+ function getEventSpeed(currentTime, event) {
62+ if (dragEvents.length != 0) {
63+ var lastDrag = dragEvents[dragEvents.length-1]
64+ var duration = Math.max(1, currentTime - lastDrag[0])
65+ return (event.y - lastDrag[2]) / duration
66+ } else {
67+ return 0
68+ }
69+ }
70+ }
71+
72+ MouseArea {
73+ id: mouseArea
74+ anchors.fill: parent
75+ hoverEnabled: true
76+ onClicked: root.clicked()
77+ }
78+
79+ MultiPointTouchArea {
80+ anchors.fill: parent
81+ mouseEnabled: false
82+ maximumTouchPoints: 1
83+
84+ onGestureStarted: {
85+ if (!d.moving) {
86+ d.moving = true
87+ gesture.grab();
88+ d.dragEvents = []
89+ }
90+ }
91+
92+ onCanceled: {
93+ d.moving = false
94+ animation.animate("center");
95+ }
96+
97+ onTouchUpdated: {
98+ if (touchPoints.length == 0 || !d.moving) {
99+ return;
100+ }
101+
102+ var touchPoint = touchPoints[0];
103+ d.distance = touchPoint.y - touchPoint.startY
104+ d.pushDragEvent(touchPoint);
105+ print("pushed event", touchPoint.y, "velocity is now", d.dragVelocity)
106+ }
107+
108+ onReleased: {
109+ print("released", d.moving)
110+ if (!d.moving) {
111+ root.clicked()
112+ }
113+
114+ if (!root.closeable) {
115+ animation.animate("center")
116+ return;
117+ }
118+
119+ var touchPoint = touchPoints[0];
120+
121+ print("released velocity is", d.dragVelocity, "min velocity", root.minSpeedToClose)
122+ if ((d.dragVelocity < -root.minSpeedToClose && d.distance < -units.gu(8)) || d.distance < -root.height / 2) {
123+ animation.animate("up")
124+ } else if ((d.dragVelocity > root.minSpeedToClose && d.distance > units.gu(8)) || d.distance > root.height / 2) {
125+ animation.animate("down")
126+ } else {
127+ animation.animate("center")
128+ }
129+ }
130+ }
131+
132+ UbuntuNumberAnimation {
133+ id: animation
134+ objectName: "closeAnimation"
135+ target: d
136+ property: "distance"
137+ property bool requestClose: false
138+
139+ function animate(direction) {
140+ animation.from = dragArea.distance;
141+ switch (direction) {
142+ case "up":
143+ animation.to = -root.height * 1.5;
144+ requestClose = true;
145+ break;
146+ case "down":
147+ animation.to = root.height * 1.5;
148+ requestClose = true;
149+ break;
150+ default:
151+ animation.to = 0
152+ }
153+ animation.start();
154+ }
155+
156+ onRunningChanged: {
157+ if (!running) {
158+ d.moving = false;
159+ if (requestClose) {
160+ root.close();
161+ } else {
162+ d.distance = 0;
163+ }
164+ }
165+ }
166+ }
167+}
168+
169+//DraggingArea {
170+// id: root
171+// onPressed: mouse.accepted = true;
172+// hoverEnabled: true
173+
174+
175+// property bool closeable: true
176+
177+// property bool moving: false
178+// property real distance: 0
179+// readonly property int threshold: units.gu(2)
180+// property int offset: 0
181+
182+// readonly property real minSpeedToClose: units.gu(40)
183+
184+// signal close();
185+
186+// onMouseYChanged: {
187+// if (pressed) {
188+// appDelegate.y
189+// }
190+// }
191+
192+// onDragValueChanged: {
193+// if (!dragging) {
194+// return;
195+// }
196+// moving = moving || Math.abs(dragValue) > threshold;
197+// if (moving) {
198+// distance = dragValue + offset;
199+// }
200+// }
201+
202+// onMovingChanged: {
203+// if (moving) {
204+// offset = (dragValue > 0 ? -threshold: threshold)
205+// } else {
206+// offset = 0;
207+// }
208+// }
209+
210+// onDragEnd: {
211+// if (!root.closeable) {
212+// animation.animate("center")
213+// return;
214+// }
215+
216+// // velocity and distance values specified by design prototype
217+// if ((dragVelocity < -minSpeedToClose && distance < -units.gu(8)) || distance < -root.height / 2) {
218+// animation.animate("up")
219+// } else if ((dragVelocity > minSpeedToClose && distance > units.gu(8)) || distance > root.height / 2) {
220+// animation.animate("down")
221+// } else {
222+// animation.animate("center")
223+// }
224+// }
225+
226+// UbuntuNumberAnimation {
227+// id: animation
228+// objectName: "closeAnimation"
229+// target: dragArea
230+// property: "distance"
231+// property bool requestClose: false
232+
233+// function animate(direction) {
234+// animation.from = dragArea.distance;
235+// switch (direction) {
236+// case "up":
237+// animation.to = -root.height * 1.5;
238+// requestClose = true;
239+// break;
240+// case "down":
241+// animation.to = root.height * 1.5;
242+// requestClose = true;
243+// break;
244+// default:
245+// animation.to = 0
246+// }
247+// animation.start();
248+// }
249+
250+// onRunningChanged: {
251+// if (!running) {
252+// dragArea.moving = false;
253+// if (requestClose) {
254+// root.close();
255+// } else {
256+// dragArea.distance = 0;
257+// }
258+// }
259+// }
260+// }
261+//}
262+