Merge lp:~dandrader/unity8/rotatedDDA into lp:unity8
- rotatedDDA
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Daniel d'Andrada |
Approved revision: | 693 |
Merged at revision: | 721 |
Proposed branch: | lp:~dandrader/unity8/rotatedDDA |
Merge into: | lp:unity8 |
Diff against target: |
941 lines (+410/-183) 12 files modified
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+105/-45) plugins/Ubuntu/Gestures/DirectionalDragArea.h (+11/-1) qml/Components/DragHandle.qml (+2/-1) tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml (+8/-1) tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml (+7/-0) tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml (+7/-0) tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml (+7/-0) tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+108/-17) tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml (+32/-10) tests/qmltests/Components/tst_DragHandle.cpp (+45/-5) tests/qmltests/Components/tst_DragHandle.qml (+78/-62) tests/qmltests/Components/tst_DragHandle/TestButton.qml (+0/-41) |
To merge this branch: | bzr merge lp:~dandrader/unity8/rotatedDDA |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Gerry Boland (community) | Approve | ||
Michael Zanetti (community) | Approve | ||
Review via email: mp+205845@code.launchpad.net |
Commit message
Make DirectionalDragArea work when rotated
The drag gesture direction is in local coordinates, not in scene coordinates
Description of the change
run "make tryDirectionalD
Then press the "rotated: 0" button to change it to "rotated: 90".
Try out dragging from the window edges.
Without this patch DirectionalDrag
As for "make tryDragHandle", when rotated, the ubuntu logos would never stay open. Once released, they would always rollback.
This is needed for the "rotated nexus 7" work.
I.e.: having a rotated unity8 shell.
e.g.: a landscape unity8 (tablet mode) on a portrait display.
* Are there any related MPs required for this MP to build/function as expected?
No
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes, with "make tryDirectionalD
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable
* If you changed the UI, has there been a design review?
Not applicable
PS Jenkins bot (ps-jenkins) wrote : | # |
Michael Zanetti (mzanetti) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yip yip. Doesn't seem to break anything on Nexus 4. Gerry will do the test for the new functionality on the rotated Nexus 7.
* Did CI run pass? If not, please explain why.
Nope :/ Unrelated test failure. It's fix is waiting to be picked up on the Train station still.
Gerry Boland (gerboland) wrote : | # |
Works good on rotated shell. Approving
Daniel d'Andrada (dandrader) wrote : | # |
There is still a bit more work to be done. Making use of this MP instead of opening another one as it hasn't been merged yet.
Daniel d'Andrada (dandrader) wrote : | # |
All fixed now.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:693
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : | # |
Approving, as works even better on rotated shell than before
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:693
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Gerry Boland (gerboland) wrote : | # |
• Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
• Did CI run pass? If not, please explain why.
It did
Preview Diff
1 | === modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp' |
2 | --- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2013-10-23 13:09:39 +0000 |
3 | +++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2014-02-12 22:13:40 +0000 |
4 | @@ -114,6 +114,7 @@ |
5 | , m_wideningAngle(0) |
6 | , m_wideningFactor(0) |
7 | , m_distanceThreshold(0) |
8 | + , m_distanceThresholdSquared(0.) |
9 | , m_minSpeed(0) |
10 | , m_maxSilenceTime(200) |
11 | , m_silenceTime(0) |
12 | @@ -162,7 +163,14 @@ |
13 | return; |
14 | |
15 | m_wideningAngle = angle; |
16 | - m_wideningFactor = qTan(angle * M_PI / 180.0); |
17 | + |
18 | + // wideningFactor = pow(cosine(angle), 2) |
19 | + { |
20 | + qreal angleRadians = angle * M_PI / 180.0; |
21 | + m_wideningFactor = qCos(angleRadians); |
22 | + m_wideningFactor = m_wideningFactor * m_wideningFactor; |
23 | + } |
24 | + |
25 | Q_EMIT wideningAngleChanged(angle); |
26 | } |
27 | |
28 | @@ -170,6 +178,7 @@ |
29 | { |
30 | if (m_distanceThreshold != value) { |
31 | m_distanceThreshold = value; |
32 | + m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold; |
33 | Q_EMIT distanceThresholdChanged(value); |
34 | } |
35 | } |
36 | @@ -237,13 +246,15 @@ |
37 | } |
38 | } |
39 | |
40 | +void DirectionalDragArea::updateSceneDistance() |
41 | +{ |
42 | + QPointF totalMovement = m_previousScenePos - m_startScenePos; |
43 | + m_sceneDistance = projectOntoDirectionVector(totalMovement); |
44 | +} |
45 | + |
46 | qreal DirectionalDragArea::sceneDistance() const |
47 | { |
48 | - if (Direction::isHorizontal(m_direction)) { |
49 | - return m_previousScenePos.x() - m_startScenePos.x(); |
50 | - } else { |
51 | - return m_previousScenePos.y() - m_startScenePos.y(); |
52 | - } |
53 | + return m_sceneDistance; |
54 | } |
55 | |
56 | qreal DirectionalDragArea::touchX() const |
57 | @@ -335,12 +346,13 @@ |
58 | m_startScenePos = newTouchPoint->scenePos(); |
59 | m_touchId = newTouchPoint->id(); |
60 | m_dampedScenePos.reset(m_startScenePos); |
61 | - updateVelocityCalculator(m_startScenePos); |
62 | + m_velocityCalculator->setTrackedPosition(0.); |
63 | m_velocityCalculator->reset(); |
64 | m_numSamplesOnLastSpeedCheck = 0; |
65 | m_silenceTime = 0; |
66 | setPreviousPos(m_startPos); |
67 | setPreviousScenePos(m_startScenePos); |
68 | + updateSceneDirectionVector(); |
69 | |
70 | setStatus(Undecided); |
71 | } |
72 | @@ -441,44 +453,53 @@ |
73 | |
74 | bool DirectionalDragArea::pointInsideAllowedArea() const |
75 | { |
76 | - qreal dX = m_dampedScenePos.x() - m_startScenePos.x(); |
77 | - qreal dY = m_dampedScenePos.y() - m_startScenePos.y(); |
78 | - |
79 | - switch (m_direction) { |
80 | - case Direction::Upwards: |
81 | - return dY <= 0 && qFabs(dX) <= qFabs(dY) * m_wideningFactor; |
82 | - case Direction::Downwards: |
83 | - return dY >= 0 && qFabs(dX) <= dY * m_wideningFactor; |
84 | - case Direction::Leftwards: |
85 | - return dX <= 0 && qFabs(dY) <= qFabs(dX) * m_wideningFactor; |
86 | - default: // Direction::Rightwards: |
87 | - return dX >= 0 && qFabs(dY) <= dX * m_wideningFactor; |
88 | + // NB: Using squared values to avoid computing the square root to find |
89 | + // the length totalMovement |
90 | + |
91 | + QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(), |
92 | + m_dampedScenePos.y() - m_startScenePos.y()); |
93 | + |
94 | + qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() + |
95 | + totalMovement.y() * totalMovement.y(); |
96 | + |
97 | + if (squaredTotalMovSize == 0.) { |
98 | + // didn't move |
99 | + return true; |
100 | } |
101 | + |
102 | + qreal projectedMovement = projectOntoDirectionVector(totalMovement); |
103 | + |
104 | + |
105 | + qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize; |
106 | + |
107 | + // Same as: |
108 | + // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle |
109 | + return cosineAngleSquared >= m_wideningFactor; |
110 | } |
111 | |
112 | bool DirectionalDragArea::movingInRightDirection() const |
113 | { |
114 | - switch (m_direction) { |
115 | - case Direction::Upwards: |
116 | - return m_dampedScenePos.y() <= m_previousDampedScenePos.y(); |
117 | - case Direction::Downwards: |
118 | - return m_dampedScenePos.y() >= m_previousDampedScenePos.y(); |
119 | - case Direction::Leftwards: |
120 | - return m_dampedScenePos.x() <= m_previousDampedScenePos.x(); |
121 | - default: // Direction::Rightwards: |
122 | - return m_dampedScenePos.x() >= m_previousDampedScenePos.x(); |
123 | - } |
124 | + QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(), |
125 | + m_dampedScenePos.y() - m_previousDampedScenePos.y()); |
126 | + |
127 | + qreal scalarProjection = projectOntoDirectionVector(movementVector); |
128 | + |
129 | + return scalarProjection >= 0.; |
130 | } |
131 | |
132 | bool DirectionalDragArea::movedFarEnough(const QPointF &point) const |
133 | { |
134 | - if (m_distanceThreshold > 0) { |
135 | - if (Direction::isHorizontal(m_direction)) |
136 | - return qFabs(point.x() - m_startScenePos.x()) > m_distanceThreshold; |
137 | - else |
138 | - return qFabs(point.y() - m_startScenePos.y()) > m_distanceThreshold; |
139 | + if (m_distanceThreshold <= 0.) { |
140 | + // distance threshold check is disabled |
141 | + return true; |
142 | } else { |
143 | - return true; |
144 | + QPointF totalMovement(point.x() - m_startScenePos.x(), |
145 | + point.y() - m_startScenePos.y()); |
146 | + |
147 | + qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() + |
148 | + totalMovement.y() * totalMovement.y(); |
149 | + |
150 | + return squaredTotalMovSize > m_distanceThresholdSquared; |
151 | } |
152 | } |
153 | |
154 | @@ -568,28 +589,33 @@ |
155 | bool xChanged = m_previousScenePos.x() != point.x(); |
156 | bool yChanged = m_previousScenePos.y() != point.y(); |
157 | |
158 | + if (!xChanged && !yChanged) |
159 | + return; |
160 | + |
161 | + qreal oldSceneDistance = sceneDistance(); |
162 | m_previousScenePos = point; |
163 | + updateSceneDistance(); |
164 | + |
165 | + if (oldSceneDistance != sceneDistance()) { |
166 | + Q_EMIT sceneDistanceChanged(sceneDistance()); |
167 | + } |
168 | |
169 | if (xChanged) { |
170 | Q_EMIT touchSceneXChanged(point.x()); |
171 | - if (Direction::isHorizontal(m_direction)) |
172 | - Q_EMIT sceneDistanceChanged(sceneDistance()); |
173 | } |
174 | |
175 | if (yChanged) { |
176 | Q_EMIT touchSceneYChanged(point.y()); |
177 | - if (Direction::isVertical(m_direction)) |
178 | - Q_EMIT sceneDistanceChanged(sceneDistance()); |
179 | } |
180 | } |
181 | |
182 | -void DirectionalDragArea::updateVelocityCalculator(const QPointF &point) |
183 | +void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos) |
184 | { |
185 | - if (Direction::isHorizontal(m_direction)) { |
186 | - m_velocityCalculator->setTrackedPosition(point.x()); |
187 | - } else { |
188 | - m_velocityCalculator->setTrackedPosition(point.y()); |
189 | - } |
190 | + QPointF totalSceneMovement = scenePos - m_startScenePos; |
191 | + |
192 | + qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement); |
193 | + |
194 | + m_velocityCalculator->setTrackedPosition(scalarProjection); |
195 | } |
196 | |
197 | bool DirectionalDragArea::isWithinTouchCompositionWindow() |
198 | @@ -693,5 +719,39 @@ |
199 | return highestStartTime; |
200 | } |
201 | |
202 | +void DirectionalDragArea::updateSceneDirectionVector() |
203 | +{ |
204 | + QPointF localOrigin(0., 0.); |
205 | + QPointF localDirection; |
206 | + switch (m_direction) { |
207 | + case Direction::Upwards: |
208 | + localDirection.rx() = 0.; |
209 | + localDirection.ry() = -1.; |
210 | + break; |
211 | + case Direction::Downwards: |
212 | + localDirection.rx() = 0.; |
213 | + localDirection.ry() = 1; |
214 | + break; |
215 | + case Direction::Leftwards: |
216 | + localDirection.rx() = -1.; |
217 | + localDirection.ry() = 0.; |
218 | + break; |
219 | + default: // Direction::Rightwards: |
220 | + localDirection.rx() = 1.; |
221 | + localDirection.ry() = 0.; |
222 | + break; |
223 | + } |
224 | + QPointF sceneOrigin = mapToScene(localOrigin); |
225 | + QPointF sceneDirection = mapToScene(localDirection); |
226 | + m_sceneDirectionVector = sceneDirection - sceneOrigin; |
227 | +} |
228 | + |
229 | +qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const |
230 | +{ |
231 | + // same as dot product as m_sceneDirectionVector is a unit vector |
232 | + return sceneVector.x() * m_sceneDirectionVector.x() + |
233 | + sceneVector.y() * m_sceneDirectionVector.y(); |
234 | +} |
235 | + |
236 | // Because we are defining a new QObject-based class (RecognitionTimer) here. |
237 | #include "DirectionalDragArea.moc" |
238 | |
239 | === modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h' |
240 | --- plugins/Ubuntu/Gestures/DirectionalDragArea.h 2013-10-23 13:09:39 +0000 |
241 | +++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2014-02-12 22:13:40 +0000 |
242 | @@ -160,6 +160,7 @@ |
243 | |
244 | qreal distance() const; |
245 | qreal sceneDistance() const; |
246 | + void updateSceneDistance(); |
247 | |
248 | qreal touchX() const; |
249 | qreal touchY() const; |
250 | @@ -231,6 +232,10 @@ |
251 | void setPreviousScenePos(const QPointF &point); |
252 | void updateVelocityCalculator(const QPointF &point); |
253 | bool isWithinTouchCompositionWindow(); |
254 | + void updateSceneDirectionVector(); |
255 | + // returns the scalar projection between the given vector (in scene coordinates) |
256 | + // and m_sceneDirectionVector |
257 | + qreal projectOntoDirectionVector(const QPointF &sceneVector) const; |
258 | |
259 | Status m_status; |
260 | |
261 | @@ -238,6 +243,7 @@ |
262 | QPointF m_startScenePos; |
263 | QPointF m_previousPos; |
264 | QPointF m_previousScenePos; |
265 | + qreal m_sceneDistance; |
266 | int m_touchId; |
267 | |
268 | // A movement damper is used in some of the gesture recognition calculations |
269 | @@ -245,10 +251,14 @@ |
270 | DampedPointF m_dampedScenePos; |
271 | QPointF m_previousDampedScenePos; |
272 | |
273 | + // Unit vector in scene coordinates describing the direction of the gesture recognition |
274 | + QPointF m_sceneDirectionVector; |
275 | + |
276 | Direction::Type m_direction; |
277 | qreal m_wideningAngle; // in degrees |
278 | - qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle)) |
279 | + qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2) |
280 | qreal m_distanceThreshold; |
281 | + qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2) |
282 | qreal m_minSpeed; |
283 | int m_maxSilenceTime; // in milliseconds |
284 | int m_silenceTime; // in milliseconds |
285 | |
286 | === modified file 'qml/Components/DragHandle.qml' |
287 | --- qml/Components/DragHandle.qml 2014-01-21 15:30:06 +0000 |
288 | +++ qml/Components/DragHandle.qml 2014-02-12 22:13:40 +0000 |
289 | @@ -167,7 +167,8 @@ |
290 | EdgeDragEvaluator { |
291 | objectName: "edgeDragEvaluator" |
292 | id: dragEvaluator |
293 | - trackedPosition: sceneDistance |
294 | + // Effectively convert distance into the drag position projected onto the gesture direction axis |
295 | + trackedPosition: Direction.isPositive(dragArea.direction) ? sceneDistance : -sceneDistance |
296 | maxDragDistance: maxTotalDragDistance |
297 | direction: dragArea.direction |
298 | } |
299 | |
300 | === modified file 'tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml' |
301 | --- tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2013-10-22 15:56:37 +0000 |
302 | +++ tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2014-02-12 22:13:40 +0000 |
303 | @@ -63,7 +63,7 @@ |
304 | dragAreaRect.opacity = 0.3 |
305 | launcher.y = Qt.binding(launcher.followDragArea) |
306 | break; |
307 | - defaut: // DirectionalDragArea.Recognized: |
308 | + default: // DirectionalDragArea.Recognized: |
309 | dragAreaRect.color = "green" |
310 | dragAreaRect.opacity = 0.5 |
311 | break; |
312 | @@ -76,4 +76,11 @@ |
313 | top: parent.top |
314 | } |
315 | } |
316 | + |
317 | + Label { |
318 | + text: "Downwards" |
319 | + anchors.top: parent.top |
320 | + anchors.horizontalCenter: parent.horizontalCenter |
321 | + anchors.topMargin: units.gu(1) |
322 | + } |
323 | } |
324 | |
325 | === modified file 'tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml' |
326 | --- tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2013-10-22 15:56:37 +0000 |
327 | +++ tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2014-02-12 22:13:40 +0000 |
328 | @@ -79,4 +79,11 @@ |
329 | bottom: parent.bottom |
330 | } |
331 | } |
332 | + |
333 | + Label { |
334 | + text: "Leftwards" |
335 | + anchors.right: parent.right |
336 | + anchors.verticalCenter: parent.verticalCenter |
337 | + anchors.rightMargin: units.gu(1) |
338 | + } |
339 | } |
340 | |
341 | === modified file 'tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml' |
342 | --- tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2013-10-22 15:56:37 +0000 |
343 | +++ tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2014-02-12 22:13:40 +0000 |
344 | @@ -77,4 +77,11 @@ |
345 | bottom: parent.bottom |
346 | } |
347 | } |
348 | + |
349 | + Label { |
350 | + text: "Rightwards" |
351 | + anchors.left: parent.left |
352 | + anchors.verticalCenter: parent.verticalCenter |
353 | + anchors.leftMargin: units.gu(1) |
354 | + } |
355 | } |
356 | |
357 | === modified file 'tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml' |
358 | --- tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2013-10-22 15:56:37 +0000 |
359 | +++ tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2014-02-12 22:13:40 +0000 |
360 | @@ -79,4 +79,11 @@ |
361 | bottom: parent.bottom |
362 | } |
363 | } |
364 | + |
365 | + Label { |
366 | + text: "Upwards" |
367 | + anchors.bottom: parent.bottom |
368 | + anchors.horizontalCenter: parent.horizontalCenter |
369 | + anchors.bottomMargin: units.gu(1) |
370 | + } |
371 | } |
372 | |
373 | === modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp' |
374 | --- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2013-11-22 13:37:25 +0000 |
375 | +++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2014-02-12 22:13:40 +0000 |
376 | @@ -83,6 +83,9 @@ |
377 | void twoFingerTap(); |
378 | void movingDDA(); |
379 | void ignoreOldFinger(); |
380 | + void rotated(); |
381 | + void sceneDistance(); |
382 | + void sceneDistance_data(); |
383 | |
384 | private: |
385 | void passTime(qint64 timeSpan); |
386 | @@ -131,18 +134,10 @@ |
387 | } |
388 | |
389 | namespace { |
390 | -QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view) |
391 | +QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea) |
392 | { |
393 | - switch (edgeDragArea->direction()) { |
394 | - case Direction::Upwards: |
395 | - return QPointF(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f)); |
396 | - case Direction::Downwards: |
397 | - return QPointF(view->width()/2.0f, edgeDragArea->height()/2.0f); |
398 | - case Direction::Leftwards: |
399 | - return QPointF(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f); |
400 | - default: // Direction::Rightwards: |
401 | - return QPointF(edgeDragArea->width()/2.0f, view->height()/2.0f); |
402 | - } |
403 | + QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.); |
404 | + return edgeDragArea->mapToScene(localCenter); |
405 | } |
406 | |
407 | QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea, |
408 | @@ -193,7 +188,7 @@ |
409 | |
410 | QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool))); |
411 | |
412 | - QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view); |
413 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
414 | QPointF touchPoint = initialTouchPos; |
415 | |
416 | qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor; |
417 | @@ -300,7 +295,7 @@ |
418 | edgeDragArea->setRecognitionTimer(fakeTimer); |
419 | edgeDragArea->setTimeSource(fakeTimeSource); |
420 | |
421 | - QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view); |
422 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
423 | QPointF touchPoint = initialTouchPos; |
424 | |
425 | qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0; |
426 | @@ -360,7 +355,7 @@ |
427 | |
428 | edgeDragArea->setMinSpeed(minSpeed); |
429 | |
430 | - QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view); |
431 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
432 | QPointF touchPoint = initialTouchPos; |
433 | |
434 | QPointF dragDirectionVector(1.0, 0.0); |
435 | @@ -408,7 +403,7 @@ |
436 | |
437 | int timeStepMs = 5; // some arbitrary small value. |
438 | |
439 | - QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view); |
440 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
441 | QPointF touchPoint = initialTouchPos; |
442 | |
443 | QPointF dragDirectionVector(1.0, 0.0); |
444 | @@ -452,7 +447,7 @@ |
445 | // Make sure this property is not disabled |
446 | edgeDragArea->setMaxSilenceTime(100); |
447 | |
448 | - QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view); |
449 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
450 | QPointF touchPoint = initialTouchPos; |
451 | |
452 | QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
453 | @@ -620,7 +615,7 @@ |
454 | edgeDragArea->setRecognitionTimer(fakeTimer); |
455 | edgeDragArea->setTimeSource(fakeTimeSource); |
456 | |
457 | - QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view); |
458 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
459 | QPointF touchPoint = initialTouchPos; |
460 | |
461 | qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f; |
462 | @@ -704,6 +699,102 @@ |
463 | QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch); |
464 | } |
465 | |
466 | +/* |
467 | + A Rightwards DDA that is rotated 90 degrees clockwise should recognize gestures |
468 | + that are done downwards in scene coordinates. I.e. the gesture recognition direction |
469 | + should be in local coordinates, not scene coordinates. |
470 | + */ |
471 | +void tst_DirectionalDragArea::rotated() |
472 | +{ |
473 | + QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem"); |
474 | + baseItem->setRotation(90.); |
475 | + |
476 | + QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher"); |
477 | + Q_ASSERT(rightwardsLauncher != 0); |
478 | + |
479 | + DirectionalDragArea *edgeDragArea = |
480 | + rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea"); |
481 | + Q_ASSERT(edgeDragArea != 0); |
482 | + edgeDragArea->setRecognitionTimer(fakeTimer); |
483 | + edgeDragArea->setTimeSource(fakeTimeSource); |
484 | + |
485 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
486 | + QPointF touchPoint = initialTouchPos; |
487 | + |
488 | + qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f; |
489 | + QPointF dragDirectionVector(0.0f, 1.0f); |
490 | + |
491 | + qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f; |
492 | + QPointF touchMovement = dragDirectionVector * movementStepDistance; |
493 | + int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance); |
494 | + int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps; |
495 | + |
496 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
497 | + |
498 | + for (int i = 0; i < totalMovementSteps; ++i) { |
499 | + touchPoint += touchMovement; |
500 | + passTime(movementTimeStepMs); |
501 | + QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint()); |
502 | + } |
503 | + |
504 | + QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized); |
505 | + |
506 | + QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint()); |
507 | +} |
508 | + |
509 | +void tst_DirectionalDragArea::sceneDistance() |
510 | +{ |
511 | + QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem"); |
512 | + QFETCH(qreal, rotation); |
513 | + QFETCH(QPointF, dragDirectionVector); |
514 | + baseItem->setRotation(rotation); |
515 | + |
516 | + QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher"); |
517 | + Q_ASSERT(rightwardsLauncher != 0); |
518 | + |
519 | + DirectionalDragArea *edgeDragArea = |
520 | + rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea"); |
521 | + Q_ASSERT(edgeDragArea != 0); |
522 | + edgeDragArea->setRecognitionTimer(fakeTimer); |
523 | + edgeDragArea->setTimeSource(fakeTimeSource); |
524 | + |
525 | + QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea); |
526 | + QPointF touchPoint = initialTouchPos; |
527 | + |
528 | + qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f; |
529 | + |
530 | + qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f; |
531 | + QPointF touchMovement = dragDirectionVector * movementStepDistance; |
532 | + int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance); |
533 | + int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps; |
534 | + |
535 | + QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint()); |
536 | + |
537 | + for (int i = 0; i < totalMovementSteps; ++i) { |
538 | + touchPoint += touchMovement; |
539 | + passTime(movementTimeStepMs); |
540 | + QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint()); |
541 | + } |
542 | + |
543 | + qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance; |
544 | + |
545 | + // DirectionalDragArea::sceneDistance() must match the actual drag distance as the |
546 | + // drag was aligned with the gesture direction |
547 | + // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken. |
548 | + QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001); |
549 | + |
550 | + QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint()); |
551 | +} |
552 | + |
553 | +void tst_DirectionalDragArea::sceneDistance_data() |
554 | +{ |
555 | + QTest::addColumn<qreal>("rotation"); |
556 | + QTest::addColumn<QPointF>("dragDirectionVector"); |
557 | + |
558 | + QTest::newRow("not rotated") << 0. << QPointF(1., 0.); |
559 | + QTest::newRow("rotated by 90 degrees") << 90. << QPointF(0., 1.); |
560 | +} |
561 | + |
562 | QTEST_MAIN(tst_DirectionalDragArea) |
563 | |
564 | #include "tst_DirectionalDragArea.moc" |
565 | |
566 | === modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml' |
567 | --- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml 2013-11-22 11:28:28 +0000 |
568 | +++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml 2014-02-12 22:13:40 +0000 |
569 | @@ -18,8 +18,8 @@ |
570 | import Ubuntu.Components 0.1 |
571 | |
572 | Rectangle { |
573 | - width: units.gu(40) |
574 | - height: units.gu(71) |
575 | + width: units.gu(60) |
576 | + height: units.gu(60) |
577 | color: "white" |
578 | |
579 | MouseArea { |
580 | @@ -33,14 +33,36 @@ |
581 | } |
582 | } |
583 | |
584 | - // NB: Do not anchor it as we will move it programmatically from the test |
585 | - RightwardsLauncher { |
586 | - id: hpLauncher; |
587 | + Item { |
588 | + id: baseItem |
589 | + objectName: "baseItem" |
590 | width: parent.width |
591 | height: parent.height |
592 | - } |
593 | - |
594 | - LeftwardsLauncher { id: hnLauncher; anchors.fill: parent } |
595 | - DownwardsLauncher { id: vpLauncher; anchors.fill: parent } |
596 | - UpwardsLauncher { id: vnLauncher; anchors.fill: parent } |
597 | + |
598 | + // NB: Do not anchor it as we will move it programmatically from the test |
599 | + RightwardsLauncher { |
600 | + id: hpLauncher; |
601 | + width: parent.width |
602 | + height: parent.height |
603 | + } |
604 | + |
605 | + LeftwardsLauncher { id: hnLauncher; anchors.fill: parent } |
606 | + DownwardsLauncher { id: vpLauncher; anchors.fill: parent } |
607 | + UpwardsLauncher { id: vnLauncher; anchors.fill: parent } |
608 | + } |
609 | + |
610 | + Button { |
611 | + anchors.bottom: parent.bottom |
612 | + anchors.left: parent.left |
613 | + anchors.margins: units.gu(1) |
614 | + |
615 | + text: "rotation: " + baseItem.rotation |
616 | + onClicked: { |
617 | + if (baseItem.rotation === 0.0) { |
618 | + baseItem.rotation = 90.0 |
619 | + } else { |
620 | + baseItem.rotation = 0.0 |
621 | + } |
622 | + } |
623 | + } |
624 | } |
625 | |
626 | === modified file 'tests/qmltests/Components/tst_DragHandle.cpp' |
627 | --- tests/qmltests/Components/tst_DragHandle.cpp 2014-01-21 15:29:34 +0000 |
628 | +++ tests/qmltests/Components/tst_DragHandle.cpp 2014-02-12 22:13:40 +0000 |
629 | @@ -58,7 +58,9 @@ |
630 | void cleanup(); // called right after each and every test function is executed |
631 | |
632 | void dragThreshold_horizontal(); |
633 | + void dragThreshold_horizontal_data(); |
634 | void dragThreshold_vertical(); |
635 | + void dragThreshold_vertical_data(); |
636 | void stretch_horizontal(); |
637 | void stretch_vertical(); |
638 | void hintingAnimation(); |
639 | @@ -145,17 +147,29 @@ |
640 | namespace { |
641 | QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea) |
642 | { |
643 | - |
644 | + QPointF localOrigin(0., 0.); |
645 | + QPointF localDirection; |
646 | switch (edgeDragArea->direction()) { |
647 | case Direction::Upwards: |
648 | - return QPointF(0, -1); |
649 | + localDirection.rx() = 0.; |
650 | + localDirection.ry() = -1.; |
651 | + break; |
652 | case Direction::Downwards: |
653 | - return QPointF(0, 1); |
654 | + localDirection.rx() = 0.; |
655 | + localDirection.ry() = 1; |
656 | + break; |
657 | case Direction::Leftwards: |
658 | - return QPointF(-1, 0); |
659 | + localDirection.rx() = -1.; |
660 | + localDirection.ry() = 0.; |
661 | + break; |
662 | default: // Direction::Rightwards: |
663 | - return QPointF(1, 0); |
664 | + localDirection.rx() = 1.; |
665 | + localDirection.ry() = 0.; |
666 | + break; |
667 | } |
668 | + QPointF sceneOrigin = edgeDragArea->mapToScene(localOrigin); |
669 | + QPointF sceneDirection = edgeDragArea->mapToScene(localDirection); |
670 | + return sceneDirection - sceneOrigin; |
671 | } |
672 | } |
673 | |
674 | @@ -222,6 +236,11 @@ |
675 | */ |
676 | void tst_DragHandle::dragThreshold_horizontal() |
677 | { |
678 | + QFETCH(qreal, rotation); |
679 | + |
680 | + QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem"); |
681 | + baseItem->setRotation(rotation); |
682 | + |
683 | DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("rightwardsDragHandle"); |
684 | |
685 | qreal dragThreshold = fetchDragThreshold(dragHandle); |
686 | @@ -260,8 +279,21 @@ |
687 | QCOMPARE(parentItem->property("shown").toBool(), false); |
688 | } |
689 | |
690 | +void tst_DragHandle::dragThreshold_horizontal_data() |
691 | +{ |
692 | + QTest::addColumn<qreal>("rotation"); |
693 | + |
694 | + QTest::newRow("not rotated") << 0.; |
695 | + QTest::newRow("rotated 90") << 90.; |
696 | +} |
697 | + |
698 | void tst_DragHandle::dragThreshold_vertical() |
699 | { |
700 | + QFETCH(qreal, rotation); |
701 | + |
702 | + QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem"); |
703 | + baseItem->setRotation(rotation); |
704 | + |
705 | DirectionalDragArea *dragHandle = fetchAndSetupDragHandle("downwardsDragHandle"); |
706 | |
707 | qreal dragThreshold = fetchDragThreshold(dragHandle); |
708 | @@ -300,6 +332,14 @@ |
709 | QCOMPARE(parentItem->property("shown").toBool(), false); |
710 | } |
711 | |
712 | +void tst_DragHandle::dragThreshold_vertical_data() |
713 | +{ |
714 | + QTest::addColumn<qreal>("rotation"); |
715 | + |
716 | + QTest::newRow("not rotated") << 0.; |
717 | + QTest::newRow("rotated 90") << 90.; |
718 | +} |
719 | + |
720 | /* |
721 | Checks that when the stretch property is true, dragging the DragHandle increases |
722 | the width or height (depending on its direction) of its parent Showable |
723 | |
724 | === modified file 'tests/qmltests/Components/tst_DragHandle.qml' |
725 | --- tests/qmltests/Components/tst_DragHandle.qml 2013-12-17 16:04:47 +0000 |
726 | +++ tests/qmltests/Components/tst_DragHandle.qml 2014-02-12 22:13:40 +0000 |
727 | @@ -29,62 +29,68 @@ |
728 | */ |
729 | Item { |
730 | id: root |
731 | - width: 700 |
732 | - height: 500 |
733 | + width: units.gu(70) |
734 | + height: units.gu(70) |
735 | |
736 | property var dragHandle |
737 | |
738 | property bool stretch: false |
739 | property real hintDisplacement: 0 |
740 | |
741 | - VerticalShowable { |
742 | - onDragHandleRecognizedGesture: { root.dragHandle = dragHandle } |
743 | - stretch: root.stretch |
744 | - hintDisplacement: root.hintDisplacement |
745 | - } |
746 | - |
747 | - HorizontalShowable { |
748 | - onDragHandleRecognizedGesture: { root.dragHandle = dragHandle } |
749 | - stretch: root.stretch |
750 | - hintDisplacement: root.hintDisplacement |
751 | - } |
752 | - |
753 | - // Visually mark drag threshold |
754 | - Rectangle { |
755 | - color: "black" |
756 | - width: 2 |
757 | - height: parent.height |
758 | - |
759 | - visible: dragHandle !== undefined && Direction.isHorizontal(dragHandle.direction) |
760 | - |
761 | - x: { |
762 | - if (dragHandle) { |
763 | - if (dragHandle.direction === Direction.Rightwards) { |
764 | - dragHandle.edgeDragEvaluator.dragThreshold; |
765 | + Item { |
766 | + id: baseItem |
767 | + objectName: "baseItem" |
768 | + anchors.fill: parent |
769 | + |
770 | + VerticalShowable { |
771 | + onDragHandleRecognizedGesture: { root.dragHandle = dragHandle } |
772 | + stretch: root.stretch |
773 | + hintDisplacement: root.hintDisplacement |
774 | + } |
775 | + |
776 | + HorizontalShowable { |
777 | + onDragHandleRecognizedGesture: { root.dragHandle = dragHandle } |
778 | + stretch: root.stretch |
779 | + hintDisplacement: root.hintDisplacement |
780 | + } |
781 | + |
782 | + // Visually mark drag threshold |
783 | + Rectangle { |
784 | + color: "black" |
785 | + width: 2 |
786 | + height: parent.height |
787 | + |
788 | + visible: dragHandle !== undefined && Direction.isHorizontal(dragHandle.direction) |
789 | + |
790 | + x: { |
791 | + if (dragHandle) { |
792 | + if (dragHandle.direction === Direction.Rightwards) { |
793 | + dragHandle.edgeDragEvaluator.dragThreshold; |
794 | + } else { |
795 | + parent.width - dragHandle.edgeDragEvaluator.dragThreshold; |
796 | + } |
797 | } else { |
798 | - parent.width - dragHandle.edgeDragEvaluator.dragThreshold; |
799 | + 0 |
800 | } |
801 | - } else { |
802 | - 0 |
803 | } |
804 | } |
805 | - } |
806 | - Rectangle { |
807 | - color: "black"; |
808 | - height: 2 |
809 | - width: parent.width |
810 | - |
811 | - visible: dragHandle !== undefined && Direction.isVertical(dragHandle.direction) |
812 | - |
813 | - y: { |
814 | - if (dragHandle) { |
815 | - if (dragHandle.direction === Direction.Downwards) { |
816 | - dragHandle.edgeDragEvaluator.dragThreshold; |
817 | + Rectangle { |
818 | + color: "black"; |
819 | + height: 2 |
820 | + width: parent.width |
821 | + |
822 | + visible: dragHandle !== undefined && Direction.isVertical(dragHandle.direction) |
823 | + |
824 | + y: { |
825 | + if (dragHandle) { |
826 | + if (dragHandle.direction === Direction.Downwards) { |
827 | + dragHandle.edgeDragEvaluator.dragThreshold; |
828 | + } else { |
829 | + parent.height - dragHandle.edgeDragEvaluator.dragThreshold; |
830 | + } |
831 | } else { |
832 | - parent.height - dragHandle.edgeDragEvaluator.dragThreshold; |
833 | + 0 |
834 | } |
835 | - } else { |
836 | - 0 |
837 | } |
838 | } |
839 | } |
840 | @@ -119,27 +125,37 @@ |
841 | } |
842 | } |
843 | |
844 | - TestButton { |
845 | - id: stretchButton |
846 | + Row { |
847 | anchors.bottom: parent.bottom |
848 | anchors.left: parent.left |
849 | anchors.margins: units.gu(1) |
850 | - |
851 | - text: root.stretch ? "stretch" : "move" |
852 | - onClicked: { root.stretch = !root.stretch; } |
853 | - } |
854 | - |
855 | - TestButton { |
856 | - anchors.bottom: parent.bottom |
857 | - anchors.left: stretchButton.right |
858 | - anchors.margins: units.gu(1) |
859 | - |
860 | - text: root.hintDisplacement > 0 ? "hint" : "no hint" |
861 | - onClicked: { |
862 | - if (root.hintDisplacement > 0) { |
863 | - root.hintDisplacement = 0; |
864 | - } else { |
865 | - root.hintDisplacement = units.gu(6); |
866 | + spacing: units.gu(1) |
867 | + |
868 | + Button { |
869 | + id: stretchButton |
870 | + text: root.stretch ? "stretch" : "move" |
871 | + onClicked: { root.stretch = !root.stretch; } |
872 | + } |
873 | + |
874 | + Button { |
875 | + text: root.hintDisplacement > 0 ? "hint" : "no hint" |
876 | + onClicked: { |
877 | + if (root.hintDisplacement > 0) { |
878 | + root.hintDisplacement = 0; |
879 | + } else { |
880 | + root.hintDisplacement = units.gu(6); |
881 | + } |
882 | + } |
883 | + } |
884 | + |
885 | + Button { |
886 | + text: "rotation: " + baseItem.rotation |
887 | + onClicked: { |
888 | + if (baseItem.rotation === 0.0) { |
889 | + baseItem.rotation = 90.0 |
890 | + } else { |
891 | + baseItem.rotation = 0.0 |
892 | + } |
893 | } |
894 | } |
895 | } |
896 | |
897 | === removed file 'tests/qmltests/Components/tst_DragHandle/TestButton.qml' |
898 | --- tests/qmltests/Components/tst_DragHandle/TestButton.qml 2013-07-01 16:32:00 +0000 |
899 | +++ tests/qmltests/Components/tst_DragHandle/TestButton.qml 1970-01-01 00:00:00 +0000 |
900 | @@ -1,41 +0,0 @@ |
901 | -/* |
902 | - * Copyright (C) 2013 Canonical, Ltd. |
903 | - * |
904 | - * This program is free software; you can redistribute it and/or modify |
905 | - * it under the terms of the GNU General Public License as published by |
906 | - * the Free Software Foundation; version 3. |
907 | - * |
908 | - * This program is distributed in the hope that it will be useful, |
909 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
910 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
911 | - * GNU General Public License for more details. |
912 | - * |
913 | - * You should have received a copy of the GNU General Public License |
914 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
915 | - */ |
916 | - |
917 | -import QtQuick 2.0 |
918 | -import Ubuntu.Components 0.1 |
919 | - |
920 | -Rectangle { |
921 | - id: rect |
922 | - width: childrenRect.width |
923 | - height: childrenRect.height |
924 | - |
925 | - property alias text: textComponent.text |
926 | - |
927 | - signal clicked |
928 | - |
929 | - color: "yellow" |
930 | - Text { |
931 | - id: textComponent |
932 | - font.pixelSize: units.gu(2) |
933 | - text: root.stretch ? "stretch" : "move" |
934 | - MouseArea { |
935 | - anchors.fill: parent |
936 | - onClicked: { |
937 | - rect.clicked() |
938 | - } |
939 | - } |
940 | - } |
941 | -} |
FAILED: Continuous integration, rev:688 jenkins. qa.ubuntu. com/job/ unity8- ci/2295/ jenkins. qa.ubuntu. com/job/ generic- mediumtests- trusty/ 3137 jenkins. qa.ubuntu. com/job/ generic- mediumtests- trusty- touch/2824 jenkins. qa.ubuntu. com/job/ unity-phablet- qmluitests- trusty/ 1165 jenkins. qa.ubuntu. com/job/ unity8- trusty- amd64-ci/ 816 jenkins. qa.ubuntu. com/job/ unity8- trusty- armhf-ci/ 820 jenkins. qa.ubuntu. com/job/ unity8- trusty- armhf-ci/ 820/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity8- trusty- i386-ci/ 816 jenkins. qa.ubuntu. com/job/ autopilot- testrunner- otto-trusty/ 2756 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- trusty- amd64/3139 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- trusty- amd64/3139/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- trusty- armhf/2826 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- trusty- armhf/2826/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- runner- mako/5235 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 3848
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity8- ci/2295/ rebuild
http://