on xenial help sign in
merge in lp:~unity-team/unity8/rotate-n7 which is not to productized
b3e6180Kevin Gunn
debian/unity8.install+1
@@ -10,6 +10,7 @@ usr/share/unity8/Launcher
10usr/share/unity8/Notifications
10usr/share/unity8/Notifications
11usr/share/unity8/Panel
11usr/share/unity8/Panel
12usr/share/unity8/Shell.qml
12usr/share/unity8/Shell.qml
13+usr/share/unity8/ShellOrientator.qml
13usr/share/unity8/SideStage
14usr/share/unity8/SideStage
14usr/share/unity8/graphics
15usr/share/unity8/graphics
15data/unity8.conf usr/share/upstart/sessions/
16data/unity8.conf usr/share/upstart/sessions/
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+104−44
@@ -114,6 +114,7 @@ DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
114 , m_wideningAngle(0)
114 , m_wideningAngle(0)
115 , m_wideningFactor(0)
115 , m_wideningFactor(0)
116 , m_distanceThreshold(0)
116 , m_distanceThreshold(0)
117+ , m_distanceThresholdSquared(0.)
117 , m_minSpeed(0)
118 , m_minSpeed(0)
118 , m_maxSilenceTime(200)
119 , m_maxSilenceTime(200)
119 , m_silenceTime(0)
120 , m_silenceTime(0)
@@ -162,7 +163,14 @@ void DirectionalDragArea::setWideningAngle(qreal angle)
162 return;
163 return;
163
164
164 m_wideningAngle = angle;
165 m_wideningAngle = angle;
166+
167+ // wideningFactor = pow(cosine(angle), 2)
168+ {
165− m_wideningFactor = qTan(angle * M_PI / 180.0);
169+ qreal angleRadians = angle * M_PI / 180.0;
170+ m_wideningFactor = qCos(angleRadians);
171+ m_wideningFactor = m_wideningFactor * m_wideningFactor;
172+ }
173+
166 Q_EMIT wideningAngleChanged(angle);
174 Q_EMIT wideningAngleChanged(angle);
167}
175}
168
176
@@ -170,6 +178,7 @@ void DirectionalDragArea::setDistanceThreshold(qreal value)
170{
178{
171 if (m_distanceThreshold != value) {
179 if (m_distanceThreshold != value) {
172 m_distanceThreshold = value;
180 m_distanceThreshold = value;
181+ m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
173 Q_EMIT distanceThresholdChanged(value);
182 Q_EMIT distanceThresholdChanged(value);
174 }
183 }
175}
184}
@@ -237,13 +246,15 @@ qreal DirectionalDragArea::distance() const
237 }
246 }
238}
247}
239
248
249+void DirectionalDragArea::updateSceneDistance()
250+{
251+ QPointF totalMovement = m_previousScenePos - m_startScenePos;
252+ m_sceneDistance = projectOntoDirectionVector(totalMovement);
253+}
254+
240qreal DirectionalDragArea::sceneDistance() const
255qreal DirectionalDragArea::sceneDistance() const
241{
256{
242− if (Direction::isHorizontal(m_direction)) {
257+ return m_sceneDistance;
243− return m_previousScenePos.x() - m_startScenePos.x();
244− } else {
245− return m_previousScenePos.y() - m_startScenePos.y();
246− }
247}
258}
248
259
249qreal DirectionalDragArea::touchX() const
260qreal DirectionalDragArea::touchX() const
@@ -335,12 +346,13 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
335 m_startScenePos = newTouchPoint->scenePos();
346 m_startScenePos = newTouchPoint->scenePos();
336 m_touchId = newTouchPoint->id();
347 m_touchId = newTouchPoint->id();
337 m_dampedScenePos.reset(m_startScenePos);
348 m_dampedScenePos.reset(m_startScenePos);
338− updateVelocityCalculator(m_startScenePos);
349+ m_velocityCalculator->setTrackedPosition(0.);
339 m_velocityCalculator->reset();
350 m_velocityCalculator->reset();
340 m_numSamplesOnLastSpeedCheck = 0;
351 m_numSamplesOnLastSpeedCheck = 0;
341 m_silenceTime = 0;
352 m_silenceTime = 0;
342 setPreviousPos(m_startPos);
353 setPreviousPos(m_startPos);
343 setPreviousScenePos(m_startScenePos);
354 setPreviousScenePos(m_startScenePos);
355+ updateSceneDirectionVector();
344
356
345 setStatus(Undecided);
357 setStatus(Undecided);
346}
358}
@@ -441,44 +453,53 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
441
453
442bool DirectionalDragArea::pointInsideAllowedArea() const
454bool DirectionalDragArea::pointInsideAllowedArea() const
443{
455{
444− qreal dX = m_dampedScenePos.x() - m_startScenePos.x();
456+ // NB: Using squared values to avoid computing the square root to find
445− qreal dY = m_dampedScenePos.y() - m_startScenePos.y();
457+ // the length totalMovement
446
458
447− switch (m_direction) {
459+ QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
448− case Direction::Upwards:
460+ m_dampedScenePos.y() - m_startScenePos.y());
449− return dY <= 0 && qFabs(dX) <= qFabs(dY) * m_wideningFactor;
461+
450− case Direction::Downwards:
462+ qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
451− return dY >= 0 && qFabs(dX) <= dY * m_wideningFactor;
463+ totalMovement.y() * totalMovement.y();
452− case Direction::Leftwards:
464+
453− return dX <= 0 && qFabs(dY) <= qFabs(dX) * m_wideningFactor;
465+ if (squaredTotalMovSize == 0.) {
454− default: // Direction::Rightwards:
466+ // didn't move
455− return dX >= 0 && qFabs(dY) <= dX * m_wideningFactor;
467+ return true;
456 }
468 }
469+
470+ qreal projectedMovement = projectOntoDirectionVector(totalMovement);
471+
472+
473+ qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
474+
475+ // Same as:
476+ // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
477+ return cosineAngleSquared >= m_wideningFactor;
457}
478}
458
479
459bool DirectionalDragArea::movingInRightDirection() const
480bool DirectionalDragArea::movingInRightDirection() const
460{
481{
461− switch (m_direction) {
462− case Direction::Upwards:
463− return m_dampedScenePos.y() <= m_previousDampedScenePos.y();
482+ QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
464− case Direction::Downwards:
465− return m_dampedScenePos.y() >= m_previousDampedScenePos.y();
483+ m_dampedScenePos.y() - m_previousDampedScenePos.y());
466− case Direction::Leftwards:
484+
467− return m_dampedScenePos.x() <= m_previousDampedScenePos.x();
485+ qreal scalarProjection = projectOntoDirectionVector(movementVector);
468− default: // Direction::Rightwards:
486+
469− return m_dampedScenePos.x() >= m_previousDampedScenePos.x();
487+ return scalarProjection >= 0.;
470− }
471}
488}
472
489
473bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
490bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
474{
491{
475− if (m_distanceThreshold > 0) {
492+ if (m_distanceThreshold <= 0.) {
476− if (Direction::isHorizontal(m_direction))
493+ // distance threshold check is disabled
477− return qFabs(point.x() - m_startScenePos.x()) > m_distanceThreshold;
478− else
479− return qFabs(point.y() - m_startScenePos.y()) > m_distanceThreshold;
480− } else {
481 return true;
494 return true;
495+ } else {
496+ QPointF totalMovement(point.x() - m_startScenePos.x(),
497+ point.y() - m_startScenePos.y());
498+
499+ qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
500+ totalMovement.y() * totalMovement.y();
501+
502+ return squaredTotalMovSize > m_distanceThresholdSquared;
482 }
503 }
483}
504}
484
505
@@ -568,28 +589,33 @@ void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
568 bool xChanged = m_previousScenePos.x() != point.x();
589 bool xChanged = m_previousScenePos.x() != point.x();
569 bool yChanged = m_previousScenePos.y() != point.y();
590 bool yChanged = m_previousScenePos.y() != point.y();
570
591
592+ if (!xChanged && !yChanged)
593+ return;
594+
595+ qreal oldSceneDistance = sceneDistance();
571 m_previousScenePos = point;
596 m_previousScenePos = point;
597+ updateSceneDistance();
598+
599+ if (oldSceneDistance != sceneDistance()) {
600+ Q_EMIT sceneDistanceChanged(sceneDistance());
601+ }
572
602
573 if (xChanged) {
603 if (xChanged) {
574 Q_EMIT touchSceneXChanged(point.x());
604 Q_EMIT touchSceneXChanged(point.x());
575− if (Direction::isHorizontal(m_direction))
576− Q_EMIT sceneDistanceChanged(sceneDistance());
577 }
605 }
578
606
579 if (yChanged) {
607 if (yChanged) {
580 Q_EMIT touchSceneYChanged(point.y());
608 Q_EMIT touchSceneYChanged(point.y());
581− if (Direction::isVertical(m_direction))
582− Q_EMIT sceneDistanceChanged(sceneDistance());
583 }
609 }
584}
610}
585
611
586−void DirectionalDragArea::updateVelocityCalculator(const QPointF &point)
612+void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
587{
613{
588− if (Direction::isHorizontal(m_direction)) {
614+ QPointF totalSceneMovement = scenePos - m_startScenePos;
589− m_velocityCalculator->setTrackedPosition(point.x());
615+
590− } else {
616+ qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
617+
591− m_velocityCalculator->setTrackedPosition(point.y());
618+ m_velocityCalculator->setTrackedPosition(scalarProjection);
592− }
593}
619}
594
620
595bool DirectionalDragArea::isWithinTouchCompositionWindow()
621bool DirectionalDragArea::isWithinTouchCompositionWindow()
@@ -693,5 +719,39 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
693 return highestStartTime;
719 return highestStartTime;
694}
720}
695
721
722+void DirectionalDragArea::updateSceneDirectionVector()
723+{
724+ QPointF localOrigin(0., 0.);
725+ QPointF localDirection;
726+ switch (m_direction) {
727+ case Direction::Upwards:
728+ localDirection.rx() = 0.;
729+ localDirection.ry() = -1.;
730+ break;
731+ case Direction::Downwards:
732+ localDirection.rx() = 0.;
733+ localDirection.ry() = 1;
734+ break;
735+ case Direction::Leftwards:
736+ localDirection.rx() = -1.;
737+ localDirection.ry() = 0.;
738+ break;
739+ default: // Direction::Rightwards:
740+ localDirection.rx() = 1.;
741+ localDirection.ry() = 0.;
742+ break;
743+ }
744+ QPointF sceneOrigin = mapToScene(localOrigin);
745+ QPointF sceneDirection = mapToScene(localDirection);
746+ m_sceneDirectionVector = sceneDirection - sceneOrigin;
747+}
748+
749+qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
750+{
751+ // same as dot product as m_sceneDirectionVector is a unit vector
752+ return sceneVector.x() * m_sceneDirectionVector.x() +
753+ sceneVector.y() * m_sceneDirectionVector.y();
754+}
755+
696// Because we are defining a new QObject-based class (RecognitionTimer) here.
756// Because we are defining a new QObject-based class (RecognitionTimer) here.
697#include "DirectionalDragArea.moc"
757#include "DirectionalDragArea.moc"
plugins/Ubuntu/Gestures/DirectionalDragArea.h+11−1
@@ -160,6 +160,7 @@ public:
160
160
161 qreal distance() const;
161 qreal distance() const;
162 qreal sceneDistance() const;
162 qreal sceneDistance() const;
163+ void updateSceneDistance();
163
164
164 qreal touchX() const;
165 qreal touchX() const;
165 qreal touchY() const;
166 qreal touchY() const;
@@ -231,6 +232,10 @@ private:
231 void setPreviousScenePos(const QPointF &point);
232 void setPreviousScenePos(const QPointF &point);
232 void updateVelocityCalculator(const QPointF &point);
233 void updateVelocityCalculator(const QPointF &point);
233 bool isWithinTouchCompositionWindow();
234 bool isWithinTouchCompositionWindow();
235+ void updateSceneDirectionVector();
236+ // returns the scalar projection between the given vector (in scene coordinates)
237+ // and m_sceneDirectionVector
238+ qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
234
239
235 Status m_status;
240 Status m_status;
236
241
@@ -238,6 +243,7 @@ private:
238 QPointF m_startScenePos;
243 QPointF m_startScenePos;
239 QPointF m_previousPos;
244 QPointF m_previousPos;
240 QPointF m_previousScenePos;
245 QPointF m_previousScenePos;
246+ qreal m_sceneDistance;
241 int m_touchId;
247 int m_touchId;
242
248
243 // A movement damper is used in some of the gesture recognition calculations
249 // A movement damper is used in some of the gesture recognition calculations
@@ -245,10 +251,14 @@ private:
245 DampedPointF m_dampedScenePos;
251 DampedPointF m_dampedScenePos;
246 QPointF m_previousDampedScenePos;
252 QPointF m_previousDampedScenePos;
247
253
254+ // Unit vector in scene coordinates describing the direction of the gesture recognition
255+ QPointF m_sceneDirectionVector;
256+
248 Direction::Type m_direction;
257 Direction::Type m_direction;
249 qreal m_wideningAngle; // in degrees
258 qreal m_wideningAngle; // in degrees
250− qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle))
259+ qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
251 qreal m_distanceThreshold;
260 qreal m_distanceThreshold;
261+ qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
252 qreal m_minSpeed;
262 qreal m_minSpeed;
253 int m_maxSilenceTime; // in milliseconds
263 int m_maxSilenceTime; // in milliseconds
254 int m_silenceTime; // in milliseconds
264 int m_silenceTime; // in milliseconds
qml/Components/ApplicationManagerWrapper.qml+8
@@ -166,4 +166,12 @@ Item {
166 }
166 }
167 return desktopFile.substring(left+1, right);
167 return desktopFile.substring(left+1, right);
168 }
168 }
169+
170+ function setMainStageAppRect(item) {
171+ ApplicationManager.setMainStageAppRect(item);
172+ }
173+
174+ function setSideStageAppRect(item) {
175+ ApplicationManager.setSideStageAppRect(item);
176+ }
169}
177}
qml/Components/ApplicationScreenshot.qml+11−7
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import QtQuick.Window 2.0
18import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
19import Unity.Application 0.1
20import Unity.Application 0.1
20
21
@@ -50,12 +51,15 @@ Item {
50 visible: applicationScreenshot.withBackground
51 visible: applicationScreenshot.withBackground
51 }
52 }
52
53
54+ OrientationHelper {
55+ orientationAngle: Screen.angleBetween(Screen.primaryOrientation, nativeOrientation) // compensate for Shell rotation
56+ transitionEnabled: false
53− ApplicationImage {
57+ ApplicationImage {
54− id: applicationImage
58+ id: applicationImage
55− objectName: "screenshot image"
59+ objectName: "screenshot image"
56− width: applicationScreenshot.application ? parent.width : 0
60+ anchors.fill: parent
57− height: applicationScreenshot.application ? parent.height : 0
58− visible: applicationScreenshot.application != null && ready
61+ visible: applicationScreenshot.application != null && ready
59− source: ApplicationManager.findApplication((application) ? application.appId : "")
62+ source: ApplicationManager.findApplication((application) ? application.appId : "")
63+ }
60 }
64 }
61}
65}
qml/Components/DragHandle.qml+2−1
@@ -167,7 +167,8 @@ EdgeDragArea {
167 EdgeDragEvaluator {
167 EdgeDragEvaluator {
168 objectName: "edgeDragEvaluator"
168 objectName: "edgeDragEvaluator"
169 id: dragEvaluator
169 id: dragEvaluator
170+ // Effectively convert distance into the drag position projected onto the gesture direction axis
170− trackedPosition: sceneDistance
171+ trackedPosition: Direction.isPositive(dragArea.direction) ? sceneDistance : -sceneDistance
171 maxDragDistance: maxTotalDragDistance
172 maxDragDistance: maxTotalDragDistance
172 direction: dragArea.direction
173 direction: dragArea.direction
173 }
174 }
qml/Panel/MenuContent.qml+1
@@ -26,6 +26,7 @@ import "../Components"
26MainView {
26MainView {
27 id: content
27 id: content
28
28
29+ nativeOrientationAngle: 0 // stop contents being re-rotated when shell in landscape
29 property QtObject indicatorsModel: null
30 property QtObject indicatorsModel: null
30 property bool __contentActive: false
31 property bool __contentActive: false
31 readonly property int currentMenuIndex: tabs.selectedTabIndex
32 readonly property int currentMenuIndex: tabs.selectedTabIndex
qml/Shell.qml+23
@@ -77,6 +77,9 @@ FocusScope {
77 // FIXME: if application focused before shell starts, shell draws on top of it only.
77 // FIXME: if application focused before shell starts, shell draws on top of it only.
78 // We should detect already running applications on shell start and bring them to the front.
78 // We should detect already running applications on shell start and bring them to the front.
79 applicationManager.unfocusCurrentApplication();
79 applicationManager.unfocusCurrentApplication();
80+
81+ applicationManager.setMainStageAppRect(mainStageAppRect);
82+ applicationManager.setSideStageAppRect(sideStageAppRect);
80 }
83 }
81
84
82 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
85 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
@@ -348,6 +351,26 @@ FocusScope {
348 orientation: Qt.Horizontal
351 orientation: Qt.Horizontal
349 }
352 }
350
353
354+ // Defines the rectangle occupied by an application on the main or side stage.
355+ // Used only to inform the ApplicationManager. Can be removed when the proper
356+ // architecture is in place (unity8 as mir compositor).
357+ Item {
358+ id: mainStageAppRect
359+ x: mainStage.x
360+ y: mainStage.y + mainStage.normalApplicationY
361+ width: mainStage.width
362+ height: mainStage.height
363+ visible: false; enabled: false
364+ }
365+ Item {
366+ id: sideStageAppRect
367+ x: sideStageRevealer.openedValue
368+ y: sideStage.y + sideStage.normalApplicationY
369+ width: sideStage.width
370+ height: sideStage.height
371+ visible: false; enabled: false
372+ }
373+
351 DragHandle {
374 DragHandle {
352 id: stagesDragHandle
375 id: stagesDragHandle
353
376
tests/qmltests/Components/tst_DragHandle/TestButton.qml → qml/ShellOrientator.qmlrenamed+13−17
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -15,27 +15,23 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import QtQuick.Window 2.0
18import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
20+import Ubuntu.Gestures 0.1
19
21
20−Rectangle {
22+Item {
21− id: rect
23+ id: orientator
22− width: childrenRect.width
23− height: childrenRect.height
24
24
25− property alias text: textComponent.text
25+ // this is only here to select the width / height of the window if not running fullscreen
26+ property bool tablet: false
27+ width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
28+ height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
26
29
27− signal clicked
30+ OrientationHelper {
28−
31+ orientationAngle: Screen.angleBetween(nativeOrientation, Screen.primaryOrientation)
29− color: "yellow"
32+ transitionEnabled: false
30− Text {
33+ Shell {
31− id: textComponent
32− font.pixelSize: units.gu(2)
33− text: root.stretch ? "stretch" : "move"
34− MouseArea {
35 anchors.fill: parent
34 anchors.fill: parent
36− onClicked: {
37− rect.clicked()
38− }
39 }
35 }
40 }
36 }
41}
37}
src/main.cpp+11−1
@@ -92,6 +92,16 @@ int startShell(int argc, const char** argv, void* server)
92 view->setFlags(Qt::FramelessWindowHint);
92 view->setFlags(Qt::FramelessWindowHint);
93 }
93 }
94
94
95+ const QByteArray orientation = qgetenv("NATIVE_ORIENTATION");
96+ Qt::ScreenOrientation orientationAngle = Qt::PortraitOrientation;
97+ if (orientation == "landscape") {
98+ orientationAngle = Qt::LandscapeOrientation;
99+ } else if (!orientation.isEmpty() && orientation != "portrait") {
100+ qDebug() << "Unknown NATIVE_ORIENTATION setting, ignoring";
101+ }
102+
103+ view->rootContext()->setContextProperty("nativeOrientation", orientationAngle);
104+
95 // You will need this if you want to interact with touch-only components using a mouse
105 // You will need this if you want to interact with touch-only components using a mouse
96 // Needed only when manually testing on a desktop.
106 // Needed only when manually testing on a desktop.
97 MouseTouchAdaptor *mouseTouchAdaptor = 0;
107 MouseTouchAdaptor *mouseTouchAdaptor = 0;
@@ -109,7 +119,7 @@ int startShell(int argc, const char** argv, void* server)
109 nativeInterface->setProperty("ubuntuSessionType", 1);
119 nativeInterface->setProperty("ubuntuSessionType", 1);
110 view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
120 view->setProperty("role", 2); // INDICATOR_ACTOR_ROLE
111
121
112− QUrl source(::qmlDirectory()+"Shell.qml");
122+ QUrl source(::qmlDirectory()+"ShellOrientator.qml");
113 prependImportPaths(view->engine(), ::overrideImportPaths());
123 prependImportPaths(view->engine(), ::overrideImportPaths());
114 appendImportPaths(view->engine(), ::fallbackImportPaths());
124 appendImportPaths(view->engine(), ::fallbackImportPaths());
115
125
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+8−1
@@ -63,7 +63,7 @@ Item {
63 dragAreaRect.opacity = 0.3
63 dragAreaRect.opacity = 0.3
64 launcher.y = Qt.binding(launcher.followDragArea)
64 launcher.y = Qt.binding(launcher.followDragArea)
65 break;
65 break;
66− defaut: // DirectionalDragArea.Recognized:
66+ default: // DirectionalDragArea.Recognized:
67 dragAreaRect.color = "green"
67 dragAreaRect.color = "green"
68 dragAreaRect.opacity = 0.5
68 dragAreaRect.opacity = 0.5
69 break;
69 break;
@@ -76,4 +76,11 @@ Item {
76 top: parent.top
76 top: parent.top
77 }
77 }
78 }
78 }
79+
80+ Label {
81+ text: "Downwards"
82+ anchors.top: parent.top
83+ anchors.horizontalCenter: parent.horizontalCenter
84+ anchors.topMargin: units.gu(1)
85+ }
79}
86}
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+7
@@ -79,4 +79,11 @@ Item {
79 bottom: parent.bottom
79 bottom: parent.bottom
80 }
80 }
81 }
81 }
82+
83+ Label {
84+ text: "Leftwards"
85+ anchors.right: parent.right
86+ anchors.verticalCenter: parent.verticalCenter
87+ anchors.rightMargin: units.gu(1)
88+ }
82}
89}
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+7
@@ -77,4 +77,11 @@ Item {
77 bottom: parent.bottom
77 bottom: parent.bottom
78 }
78 }
79 }
79 }
80+
81+ Label {
82+ text: "Rightwards"
83+ anchors.left: parent.left
84+ anchors.verticalCenter: parent.verticalCenter
85+ anchors.leftMargin: units.gu(1)
86+ }
80}
87}
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+7
@@ -79,4 +79,11 @@ Item {
79 bottom: parent.bottom
79 bottom: parent.bottom
80 }
80 }
81 }
81 }
82+
83+ Label {
84+ text: "Upwards"
85+ anchors.bottom: parent.bottom
86+ anchors.horizontalCenter: parent.horizontalCenter
87+ anchors.bottomMargin: units.gu(1)
88+ }
82}
89}
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+108−17
@@ -83,6 +83,9 @@ private Q_SLOTS:
83 void twoFingerTap();
83 void twoFingerTap();
84 void movingDDA();
84 void movingDDA();
85 void ignoreOldFinger();
85 void ignoreOldFinger();
86+ void rotated();
87+ void sceneDistance();
88+ void sceneDistance_data();
86
89
87private:
90private:
88 void passTime(qint64 timeSpan);
91 void passTime(qint64 timeSpan);
@@ -131,18 +134,10 @@ void tst_DirectionalDragArea::passTime(qint64 timeSpan)
131}
134}
132
135
133namespace {
136namespace {
134−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
137+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
135{
138{
136− switch (edgeDragArea->direction()) {
139+ QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
137− case Direction::Upwards:
138− return QPointF(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
139− case Direction::Downwards:
140− return QPointF(view->width()/2.0f, edgeDragArea->height()/2.0f);
141− case Direction::Leftwards:
142− return QPointF(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
143− default: // Direction::Rightwards:
144− return QPointF(edgeDragArea->width()/2.0f, view->height()/2.0f);
140+ return edgeDragArea->mapToScene(localCenter);
145− }
146}
141}
147
142
148QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
143QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
@@ -193,7 +188,7 @@ void tst_DirectionalDragArea::edgeDrag()
193
188
194 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
189 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
195
190
196− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
191+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
197 QPointF touchPoint = initialTouchPos;
192 QPointF touchPoint = initialTouchPos;
198
193
199 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
194 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
@@ -300,7 +295,7 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
300 edgeDragArea->setRecognitionTimer(fakeTimer);
295 edgeDragArea->setRecognitionTimer(fakeTimer);
301 edgeDragArea->setTimeSource(fakeTimeSource);
296 edgeDragArea->setTimeSource(fakeTimeSource);
302
297
303− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
298+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
304 QPointF touchPoint = initialTouchPos;
299 QPointF touchPoint = initialTouchPos;
305
300
306 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
301 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
@@ -360,7 +355,7 @@ void tst_DirectionalDragArea::minSpeed()
360
355
361 edgeDragArea->setMinSpeed(minSpeed);
356 edgeDragArea->setMinSpeed(minSpeed);
362
357
363− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
358+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
364 QPointF touchPoint = initialTouchPos;
359 QPointF touchPoint = initialTouchPos;
365
360
366 QPointF dragDirectionVector(1.0, 0.0);
361 QPointF dragDirectionVector(1.0, 0.0);
@@ -408,7 +403,7 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
408
403
409 int timeStepMs = 5; // some arbitrary small value.
404 int timeStepMs = 5; // some arbitrary small value.
410
405
411− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
406+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
412 QPointF touchPoint = initialTouchPos;
407 QPointF touchPoint = initialTouchPos;
413
408
414 QPointF dragDirectionVector(1.0, 0.0);
409 QPointF dragDirectionVector(1.0, 0.0);
@@ -452,7 +447,7 @@ void tst_DirectionalDragArea::maxSilenceTime()
452 // Make sure this property is not disabled
447 // Make sure this property is not disabled
453 edgeDragArea->setMaxSilenceTime(100);
448 edgeDragArea->setMaxSilenceTime(100);
454
449
455− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
450+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
456 QPointF touchPoint = initialTouchPos;
451 QPointF touchPoint = initialTouchPos;
457
452
458 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
453 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -620,7 +615,7 @@ void tst_DirectionalDragArea::movingDDA()
620 edgeDragArea->setRecognitionTimer(fakeTimer);
615 edgeDragArea->setRecognitionTimer(fakeTimer);
621 edgeDragArea->setTimeSource(fakeTimeSource);
616 edgeDragArea->setTimeSource(fakeTimeSource);
622
617
623− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
618+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
624 QPointF touchPoint = initialTouchPos;
619 QPointF touchPoint = initialTouchPos;
625
620
626 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
621 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
@@ -704,6 +699,102 @@ void tst_DirectionalDragArea::ignoreOldFinger()
704 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
699 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
705}
700}
706
701
702+/*
703+ A Rightwards DDA that is rotated 90 degrees clockwise should recognize gestures
704+ that are done downwards in scene coordinates. I.e. the gesture recognition direction
705+ should be in local coordinates, not scene coordinates.
706+ */
707+void tst_DirectionalDragArea::rotated()
708+{
709+ QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
710+ baseItem->setRotation(90.);
711+
712+ QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
713+ Q_ASSERT(rightwardsLauncher != 0);
714+
715+ DirectionalDragArea *edgeDragArea =
716+ rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
717+ Q_ASSERT(edgeDragArea != 0);
718+ edgeDragArea->setRecognitionTimer(fakeTimer);
719+ edgeDragArea->setTimeSource(fakeTimeSource);
720+
721+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
722+ QPointF touchPoint = initialTouchPos;
723+
724+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
725+ QPointF dragDirectionVector(0.0f, 1.0f);
726+
727+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
728+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
729+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
730+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
731+
732+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
733+
734+ for (int i = 0; i < totalMovementSteps; ++i) {
735+ touchPoint += touchMovement;
736+ passTime(movementTimeStepMs);
737+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
738+ }
739+
740+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
741+
742+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
743+}
744+
745+void tst_DirectionalDragArea::sceneDistance()
746+{
747+ QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
748+ QFETCH(qreal, rotation);
749+ QFETCH(QPointF, dragDirectionVector);
750+ baseItem->setRotation(rotation);
751+
752+ QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
753+ Q_ASSERT(rightwardsLauncher != 0);
754+
755+ DirectionalDragArea *edgeDragArea =
756+ rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
757+ Q_ASSERT(edgeDragArea != 0);
758+ edgeDragArea->setRecognitionTimer(fakeTimer);
759+ edgeDragArea->setTimeSource(fakeTimeSource);
760+
761+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
762+ QPointF touchPoint = initialTouchPos;
763+
764+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
765+
766+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
767+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
768+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
769+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
770+
771+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
772+
773+ for (int i = 0; i < totalMovementSteps; ++i) {
774+ touchPoint += touchMovement;
775+ passTime(movementTimeStepMs);
776+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
777+ }
778+
779+ qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
780+
781+ // DirectionalDragArea::sceneDistance() must match the actual drag distance as the
782+ // drag was aligned with the gesture direction
783+ // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
784+ QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
785+
786+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
787+}
788+
789+void tst_DirectionalDragArea::sceneDistance_data()
790+{
791+ QTest::addColumn<qreal>("rotation");
792+ QTest::addColumn<QPointF>("dragDirectionVector");
793+
794+ QTest::newRow("not rotated") << 0. << QPointF(1., 0.);
795+ QTest::newRow("rotated by 90 degrees") << 90. << QPointF(0., 1.);
796+}
797+
707QTEST_MAIN(tst_DirectionalDragArea)
798QTEST_MAIN(tst_DirectionalDragArea)
708
799
709#include "tst_DirectionalDragArea.moc"
800#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml+30−8
@@ -18,8 +18,8 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19
19
20Rectangle {
20Rectangle {
21− width: units.gu(40)
21+ width: units.gu(60)
22− height: units.gu(71)
22+ height: units.gu(60)
23 color: "white"
23 color: "white"
24
24
25 MouseArea {
25 MouseArea {
@@ -33,14 +33,36 @@ Rectangle {
33 }
33 }
34 }
34 }
35
35
36− // NB: Do not anchor it as we will move it programmatically from the test
36+ Item {
37− RightwardsLauncher {
37+ id: baseItem
38− id: hpLauncher;
38+ objectName: "baseItem"
39 width: parent.width
39 width: parent.width
40 height: parent.height
40 height: parent.height
41+
42+ // NB: Do not anchor it as we will move it programmatically from the test
43+ RightwardsLauncher {
44+ id: hpLauncher;
45+ width: parent.width
46+ height: parent.height
47+ }
48+
49+ LeftwardsLauncher { id: hnLauncher; anchors.fill: parent }
50+ DownwardsLauncher { id: vpLauncher; anchors.fill: parent }
51+ UpwardsLauncher { id: vnLauncher; anchors.fill: parent }
41 }
52 }
42
53
43− LeftwardsLauncher { id: hnLauncher; anchors.fill: parent }
54+ Button {
44− DownwardsLauncher { id: vpLauncher; anchors.fill: parent }
55+ anchors.bottom: parent.bottom
45− UpwardsLauncher { id: vnLauncher; anchors.fill: parent }
56+ anchors.left: parent.left
57+ anchors.margins: units.gu(1)
58+
59+ text: "rotation: " + baseItem.rotation
60+ onClicked: {
61+ if (baseItem.rotation === 0.0) {
62+ baseItem.rotation = 90.0
63+ } else {
64+ baseItem.rotation = 0.0
65+ }
66+ }
67+ }
46}
68}
tests/qmltests/Components/tst_DragHandle.qml+71−56
@@ -29,62 +29,67 @@ import Ubuntu.Gestures 0.1
29 */
29 */
30Item {
30Item {
31 id: root
31 id: root
32− width: 700
32+ width: units.gu(70)
33− height: 500
33+ height: units.gu(70)
34
34
35 property var dragHandle
35 property var dragHandle
36
36
37 property bool stretch: false
37 property bool stretch: false
38 property real hintDisplacement: 0
38 property real hintDisplacement: 0
39
39
40− VerticalShowable {
40+ Item {
41− onDragHandleRecognizedGesture: { root.dragHandle = dragHandle }
41+ id: baseItem
42− stretch: root.stretch
42+ anchors.fill: parent
43− hintDisplacement: root.hintDisplacement
44− }
45−
46− HorizontalShowable {
47− onDragHandleRecognizedGesture: { root.dragHandle = dragHandle }
48− stretch: root.stretch
49− hintDisplacement: root.hintDisplacement
50− }
51
43
52− // Visually mark drag threshold
44+ VerticalShowable {
53− Rectangle {
45+ onDragHandleRecognizedGesture: { root.dragHandle = dragHandle }
54− color: "black"
46+ stretch: root.stretch
55− width: 2
47+ hintDisplacement: root.hintDisplacement
56− height: parent.height
48+ }
57
49
58− visible: dragHandle !== undefined && Direction.isHorizontal(dragHandle.direction)
50+ HorizontalShowable {
51+ onDragHandleRecognizedGesture: { root.dragHandle = dragHandle }
52+ stretch: root.stretch
53+ hintDisplacement: root.hintDisplacement
54+ }
59
55
56+ // Visually mark drag threshold
57+ Rectangle {
58+ color: "black"
59+ width: 2
60+ height: parent.height
61+
62+ visible: dragHandle !== undefined && Direction.isHorizontal(dragHandle.direction)
63+
60− x: {
64+ x: {
61− if (dragHandle) {
65+ if (dragHandle) {
62− if (dragHandle.direction === Direction.Rightwards) {
66+ if (dragHandle.direction === Direction.Rightwards) {
67+ dragHandle.edgeDragEvaluator.dragThreshold;
68+ } else {
63− dragHandle.edgeDragEvaluator.dragThreshold;
69+ parent.width - dragHandle.edgeDragEvaluator.dragThreshold;
70+ }
64 } else {
71 } else {
65− parent.width - dragHandle.edgeDragEvaluator.dragThreshold;
72+ 0
66 }
73 }
67− } else {
68− 0
69 }
74 }
70 }
75 }
71− }
72− Rectangle {
76+ Rectangle {
73− color: "black";
77+ color: "black";
74− height: 2
78+ height: 2
75− width: parent.width
79+ width: parent.width
76−
80+
77− visible: dragHandle !== undefined && Direction.isVertical(dragHandle.direction)
81+ visible: dragHandle !== undefined && Direction.isVertical(dragHandle.direction)
78−
82+
79− y: {
83+ y: {
80− if (dragHandle) {
84+ if (dragHandle) {
81− if (dragHandle.direction === Direction.Downwards) {
85+ if (dragHandle.direction === Direction.Downwards) {
86+ dragHandle.edgeDragEvaluator.dragThreshold;
87+ } else {
82− dragHandle.edgeDragEvaluator.dragThreshold;
88+ parent.height - dragHandle.edgeDragEvaluator.dragThreshold;
89+ }
83 } else {
90 } else {
84− parent.height - dragHandle.edgeDragEvaluator.dragThreshold;
91+ 0
85 }
92 }
86− } else {
87− 0
88 }
93 }
89 }
94 }
90 }
95 }
@@ -119,27 +124,37 @@ Item {
119 }
124 }
120 }
125 }
121
126
122− TestButton {
127+ Row {
123− id: stretchButton
124 anchors.bottom: parent.bottom
128 anchors.bottom: parent.bottom
125 anchors.left: parent.left
129 anchors.left: parent.left
126 anchors.margins: units.gu(1)
130 anchors.margins: units.gu(1)
131+ spacing: units.gu(1)
127
132
133+ Button {
134+ id: stretchButton
128− text: root.stretch ? "stretch" : "move"
135+ text: root.stretch ? "stretch" : "move"
129− onClicked: { root.stretch = !root.stretch; }
136+ onClicked: { root.stretch = !root.stretch; }
130− }
137+ }
131
138
132− TestButton {
139+ Button {
133− anchors.bottom: parent.bottom
140+ text: root.hintDisplacement > 0 ? "hint" : "no hint"
134− anchors.left: stretchButton.right
141+ onClicked: {
135− anchors.margins: units.gu(1)
142+ if (root.hintDisplacement > 0) {
143+ root.hintDisplacement = 0;
144+ } else {
145+ root.hintDisplacement = units.gu(6);
146+ }
147+ }
148+ }
136
149
137− text: root.hintDisplacement > 0 ? "hint" : "no hint"
150+ Button {
151+ text: "rotation: " + baseItem.rotation
138− onClicked: {
152+ onClicked: {
139− if (root.hintDisplacement > 0) {
153+ if (baseItem.rotation === 0.0) {
140− root.hintDisplacement = 0;
154+ baseItem.rotation = 90.0
141− } else {
155+ } else {
142− root.hintDisplacement = units.gu(6);
156+ baseItem.rotation = 0.0
157+ }
143 }
158 }
144 }
159 }
145 }
160 }