on xenial help sign in
Make DirectionalDragArea work when rotated
37a0c68Daniel d'Andrada
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+88−35
@@ -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}
@@ -335,12 +344,13 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
335 m_startScenePos = newTouchPoint->scenePos();
344 m_startScenePos = newTouchPoint->scenePos();
336 m_touchId = newTouchPoint->id();
345 m_touchId = newTouchPoint->id();
337 m_dampedScenePos.reset(m_startScenePos);
346 m_dampedScenePos.reset(m_startScenePos);
338− updateVelocityCalculator(m_startScenePos);
347+ m_velocityCalculator->setTrackedPosition(0.);
339 m_velocityCalculator->reset();
348 m_velocityCalculator->reset();
340 m_numSamplesOnLastSpeedCheck = 0;
349 m_numSamplesOnLastSpeedCheck = 0;
341 m_silenceTime = 0;
350 m_silenceTime = 0;
342 setPreviousPos(m_startPos);
351 setPreviousPos(m_startPos);
343 setPreviousScenePos(m_startScenePos);
352 setPreviousScenePos(m_startScenePos);
353+ updateSceneDirectionVector();
344
354
345 setStatus(Undecided);
355 setStatus(Undecided);
346}
356}
@@ -441,44 +451,53 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
441
451
442bool DirectionalDragArea::pointInsideAllowedArea() const
452bool DirectionalDragArea::pointInsideAllowedArea() const
443{
453{
444− qreal dX = m_dampedScenePos.x() - m_startScenePos.x();
454+ // NB: Using squared values to avoid computing the square root to find
445− qreal dY = m_dampedScenePos.y() - m_startScenePos.y();
455+ // the length totalMovement
446
456
447− switch (m_direction) {
457+ QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
448− case Direction::Upwards:
458+ m_dampedScenePos.y() - m_startScenePos.y());
449− return dY <= 0 && qFabs(dX) <= qFabs(dY) * m_wideningFactor;
459+
450− case Direction::Downwards:
460+ qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
451− return dY >= 0 && qFabs(dX) <= dY * m_wideningFactor;
461+ totalMovement.y() * totalMovement.y();
452− case Direction::Leftwards:
462+
453− return dX <= 0 && qFabs(dY) <= qFabs(dX) * m_wideningFactor;
463+ if (squaredTotalMovSize == 0.) {
454− default: // Direction::Rightwards:
464+ // didn't move
455− return dX >= 0 && qFabs(dY) <= dX * m_wideningFactor;
465+ return true;
456 }
466 }
467+
468+ qreal projectedMovement = projectOntoDirectionVector(totalMovement);
469+
470+
471+ qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
472+
473+ // Same as:
474+ // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
475+ return cosineAngleSquared >= m_wideningFactor;
457}
476}
458
477
459bool DirectionalDragArea::movingInRightDirection() const
478bool DirectionalDragArea::movingInRightDirection() const
460{
479{
461− switch (m_direction) {
462− case Direction::Upwards:
463− return m_dampedScenePos.y() <= m_previousDampedScenePos.y();
480+ QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
464− case Direction::Downwards:
465− return m_dampedScenePos.y() >= m_previousDampedScenePos.y();
481+ m_dampedScenePos.y() - m_previousDampedScenePos.y());
466− case Direction::Leftwards:
482+
467− return m_dampedScenePos.x() <= m_previousDampedScenePos.x();
483+ qreal scalarProjection = projectOntoDirectionVector(movementVector);
468− default: // Direction::Rightwards:
484+
469− return m_dampedScenePos.x() >= m_previousDampedScenePos.x();
485+ return scalarProjection >= 0.;
470− }
471}
486}
472
487
473bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
488bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
474{
489{
475− if (m_distanceThreshold > 0) {
490+ if (m_distanceThreshold <= 0.) {
476− if (Direction::isHorizontal(m_direction))
491+ // 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;
492 return true;
493+ } else {
494+ QPointF totalMovement(point.x() - m_startScenePos.x(),
495+ point.y() - m_startScenePos.y());
496+
497+ qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
498+ totalMovement.y() * totalMovement.y();
499+
500+ return squaredTotalMovSize > m_distanceThresholdSquared;
482 }
501 }
483}
502}
484
503
@@ -583,13 +602,13 @@ void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
583 }
602 }
584}
603}
585
604
586−void DirectionalDragArea::updateVelocityCalculator(const QPointF &point)
605+void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
587{
606{
588− if (Direction::isHorizontal(m_direction)) {
607+ QPointF totalSceneMovement = scenePos - m_startScenePos;
589− m_velocityCalculator->setTrackedPosition(point.x());
608+
590− } else {
609+ qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
610+
591− m_velocityCalculator->setTrackedPosition(point.y());
611+ m_velocityCalculator->setTrackedPosition(scalarProjection);
592− }
593}
612}
594
613
595bool DirectionalDragArea::isWithinTouchCompositionWindow()
614bool DirectionalDragArea::isWithinTouchCompositionWindow()
@@ -693,5 +712,39 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
693 return highestStartTime;
712 return highestStartTime;
694}
713}
695
714
715+void DirectionalDragArea::updateSceneDirectionVector()
716+{
717+ QPointF localOrigin(0., 0.);
718+ QPointF localDirection;
719+ switch (m_direction) {
720+ case Direction::Upwards:
721+ localDirection.rx() = 0.;
722+ localDirection.ry() = -1.;
723+ break;
724+ case Direction::Downwards:
725+ localDirection.rx() = 0.;
726+ localDirection.ry() = 1;
727+ break;
728+ case Direction::Leftwards:
729+ localDirection.rx() = -1.;
730+ localDirection.ry() = 0.;
731+ break;
732+ default: // Direction::Rightwards:
733+ localDirection.rx() = 1.;
734+ localDirection.ry() = 0.;
735+ break;
736+ }
737+ QPointF sceneOrigin = mapToScene(localOrigin);
738+ QPointF sceneDirection = mapToScene(localDirection);
739+ m_sceneDirectionVector = sceneDirection - sceneOrigin;
740+}
741+
742+qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
743+{
744+ // same as dot product as m_sceneDirectionVector is a unit vector
745+ return sceneVector.x() * m_sceneDirectionVector.x() +
746+ sceneVector.y() * m_sceneDirectionVector.y();
747+}
748+
696// Because we are defining a new QObject-based class (RecognitionTimer) here.
749// Because we are defining a new QObject-based class (RecognitionTimer) here.
697#include "DirectionalDragArea.moc"
750#include "DirectionalDragArea.moc"
plugins/Ubuntu/Gestures/DirectionalDragArea.h+9−1
@@ -231,6 +231,10 @@ private:
231 void setPreviousScenePos(const QPointF &point);
231 void setPreviousScenePos(const QPointF &point);
232 void updateVelocityCalculator(const QPointF &point);
232 void updateVelocityCalculator(const QPointF &point);
233 bool isWithinTouchCompositionWindow();
233 bool isWithinTouchCompositionWindow();
234+ void updateSceneDirectionVector();
235+ // returns the scalar projection between the given vector (in scene coordinates)
236+ // and m_sceneDirectionVector
237+ qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
234
238
235 Status m_status;
239 Status m_status;
236
240
@@ -245,10 +249,14 @@ private:
245 DampedPointF m_dampedScenePos;
249 DampedPointF m_dampedScenePos;
246 QPointF m_previousDampedScenePos;
250 QPointF m_previousDampedScenePos;
247
251
252+ // Unit vector in scene coordinates describing the direction of the gesture recognition
253+ QPointF m_sceneDirectionVector;
254+
248 Direction::Type m_direction;
255 Direction::Type m_direction;
249 qreal m_wideningAngle; // in degrees
256 qreal m_wideningAngle; // in degrees
250− qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle))
257+ qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
251 qreal m_distanceThreshold;
258 qreal m_distanceThreshold;
259+ qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
252 qreal m_minSpeed;
260 qreal m_minSpeed;
253 int m_maxSilenceTime; // in milliseconds
261 int m_maxSilenceTime; // in milliseconds
254 int m_silenceTime; // in milliseconds
262 int m_silenceTime; // in milliseconds
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+53−17
@@ -83,6 +83,7 @@ private Q_SLOTS:
83 void twoFingerTap();
83 void twoFingerTap();
84 void movingDDA();
84 void movingDDA();
85 void ignoreOldFinger();
85 void ignoreOldFinger();
86+ void rotated();
86
87
87private:
88private:
88 void passTime(qint64 timeSpan);
89 void passTime(qint64 timeSpan);
@@ -131,18 +132,10 @@ void tst_DirectionalDragArea::passTime(qint64 timeSpan)
131}
132}
132
133
133namespace {
134namespace {
134−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
135+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
135{
136{
136− switch (edgeDragArea->direction()) {
137+ 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);
138+ return edgeDragArea->mapToScene(localCenter);
145− }
146}
139}
147
140
148QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
141QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
@@ -193,7 +186,7 @@ void tst_DirectionalDragArea::edgeDrag()
193
186
194 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
187 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
195
188
196− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
189+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
197 QPointF touchPoint = initialTouchPos;
190 QPointF touchPoint = initialTouchPos;
198
191
199 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
192 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
@@ -300,7 +293,7 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
300 edgeDragArea->setRecognitionTimer(fakeTimer);
293 edgeDragArea->setRecognitionTimer(fakeTimer);
301 edgeDragArea->setTimeSource(fakeTimeSource);
294 edgeDragArea->setTimeSource(fakeTimeSource);
302
295
303− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
296+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
304 QPointF touchPoint = initialTouchPos;
297 QPointF touchPoint = initialTouchPos;
305
298
306 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
299 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
@@ -360,7 +353,7 @@ void tst_DirectionalDragArea::minSpeed()
360
353
361 edgeDragArea->setMinSpeed(minSpeed);
354 edgeDragArea->setMinSpeed(minSpeed);
362
355
363− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
356+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
364 QPointF touchPoint = initialTouchPos;
357 QPointF touchPoint = initialTouchPos;
365
358
366 QPointF dragDirectionVector(1.0, 0.0);
359 QPointF dragDirectionVector(1.0, 0.0);
@@ -408,7 +401,7 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
408
401
409 int timeStepMs = 5; // some arbitrary small value.
402 int timeStepMs = 5; // some arbitrary small value.
410
403
411− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
404+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
412 QPointF touchPoint = initialTouchPos;
405 QPointF touchPoint = initialTouchPos;
413
406
414 QPointF dragDirectionVector(1.0, 0.0);
407 QPointF dragDirectionVector(1.0, 0.0);
@@ -452,7 +445,7 @@ void tst_DirectionalDragArea::maxSilenceTime()
452 // Make sure this property is not disabled
445 // Make sure this property is not disabled
453 edgeDragArea->setMaxSilenceTime(100);
446 edgeDragArea->setMaxSilenceTime(100);
454
447
455− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
448+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
456 QPointF touchPoint = initialTouchPos;
449 QPointF touchPoint = initialTouchPos;
457
450
458 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
451 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -620,7 +613,7 @@ void tst_DirectionalDragArea::movingDDA()
620 edgeDragArea->setRecognitionTimer(fakeTimer);
613 edgeDragArea->setRecognitionTimer(fakeTimer);
621 edgeDragArea->setTimeSource(fakeTimeSource);
614 edgeDragArea->setTimeSource(fakeTimeSource);
622
615
623− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, m_view);
616+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
624 QPointF touchPoint = initialTouchPos;
617 QPointF touchPoint = initialTouchPos;
625
618
626 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
619 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
@@ -704,6 +697,49 @@ void tst_DirectionalDragArea::ignoreOldFinger()
704 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
697 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
705}
698}
706
699
700+/*
701+ A Rightwards DDA that is rotated 90 degrees clockwise should recognize gestures
702+ that are done downwards in scene coordinates. I.e. the gesture recognition direction
703+ should be in local coordinates, not scene coordinates.
704+ */
705+void tst_DirectionalDragArea::rotated()
706+{
707+ QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
708+ baseItem->setRotation(90.);
709+
710+ QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
711+ Q_ASSERT(rightwardsLauncher != 0);
712+
713+ DirectionalDragArea *edgeDragArea =
714+ rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
715+ Q_ASSERT(edgeDragArea != 0);
716+ edgeDragArea->setRecognitionTimer(fakeTimer);
717+ edgeDragArea->setTimeSource(fakeTimeSource);
718+
719+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
720+ QPointF touchPoint = initialTouchPos;
721+
722+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
723+ QPointF dragDirectionVector(0.0f, 1.0f);
724+
725+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
726+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
727+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
728+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
729+
730+ QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
731+
732+ for (int i = 0; i < totalMovementSteps; ++i) {
733+ touchPoint += touchMovement;
734+ passTime(movementTimeStepMs);
735+ QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
736+ }
737+
738+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
739+
740+ QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
741+}
742+
707QTEST_MAIN(tst_DirectionalDragArea)
743QTEST_MAIN(tst_DirectionalDragArea)
708
744
709#include "tst_DirectionalDragArea.moc"
745#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}