Merge lp:~dandrader/unity/phablet_edgeDragGesture into lp:unity/phablet

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Daniel d'Andrada
Approved revision: no longer in the source branch.
Merged at revision: 681
Proposed branch: lp:~dandrader/unity/phablet_edgeDragGesture
Merge into: lp:unity/phablet
Diff against target: 1849 lines (+1729/-0)
22 files modified
debian/qml-phone-shell.install (+1/-0)
doc/DirectionalDragArea.svg (+311/-0)
plugins/CMakeLists.txt (+1/-0)
plugins/Ubuntu/CMakeLists.txt (+1/-0)
plugins/Ubuntu/Gestures/CMakeLists.txt (+30/-0)
plugins/Ubuntu/Gestures/Damper.h (+85/-0)
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+292/-0)
plugins/Ubuntu/Gestures/DirectionalDragArea.h (+179/-0)
plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h (+23/-0)
plugins/Ubuntu/Gestures/plugin.cpp (+25/-0)
plugins/Ubuntu/Gestures/plugin.h (+31/-0)
plugins/Ubuntu/Gestures/qmldir (+2/-0)
tests/plugins/CMakeLists.txt (+1/-0)
tests/plugins/Ubuntu/CMakeLists.txt (+1/-0)
tests/plugins/Ubuntu/Gestures/CMakeLists.txt (+38/-0)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml (+87/-0)
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml (+90/-0)
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml (+86/-0)
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml (+90/-0)
tests/plugins/Ubuntu/Gestures/edgeDragExample.qml (+40/-0)
tests/plugins/Ubuntu/Gestures/tst_Damper.cpp (+40/-0)
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+275/-0)
To merge this branch: bzr merge lp:~dandrader/unity/phablet_edgeDragGesture
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Michał Sawicz Approve
Review via email: mp+162883@code.launchpad.net

Commit message

New: DirectionalDragArea component

An area that detects axis-aligned single-finger drag gestures.

Description of the change

New: DirectionalDragArea component

An area that detects axis-aligned single-finger drag gestures.

To be used for edge-drag gestures (like bringing in the launcher).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :
Download full text (5.0 KiB)

* instead of the pair direction, positiveDirection, I'd go for a single enum with 4 values, what do you think?
* we talked yesterday about the possibility to add a time threshold as well (within which the touch must cross the distance threshold), do you think you could add it here or in a later change?
* there seems to be a bunch of things common with the QtWidgets QGestureRecognizer (state names, for example) [1] - do you think it would make sense to converge on the same terminology?
* could you add some newlines to tests/plugins/UbuntuGestures/CMakeLists.txt for readability?

=====

 360 +add_definitions(-DUBUNTUGESTURES_LIBRARY)
 361 +
 362 +add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES})

 749 +class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {

 856 +#if defined(UBUNTUGESTURES_LIBRARY)
 857 +# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT
 858 +#else
 859 +# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT
 860 +#endif

 973 + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures

 982 +target_link_libraries(DirectionalDragAreaTestExec UbuntuGestureQml)

maybe directly use the .cpp in test sources instead? We don't really want it to be a library, do we.

=====

 538 + if (touchPoint->state() == Qt::TouchPointReleased) {

is it not possible here for touchPoint to be invalid? why wouldn't QTouchEvent::touchPoints() be enough here?

=====

I'm wondering if the "pointInside..." calculations couldn't be simplified by using a qreal factor instead of the angle - you'd just need to make sure that:

-d <= Δy <= Δx * f + d

where y is perpendicular to the gesture direction (horizontal positive / left-to-right in that case), d is max deviation, f is the factor; with f = 1 that would mean 45° widening angle; what do you think?

=====

 666 +bool DirectionalDragArea::movingInRightDirection(const QPointF &point)
 667 +{
 668 + if (m_orientation == Horizontal) {
 669 + if (m_positiveDirection) {
 670 + return point.x() >= m_previousPos.x();

shouldn't this take some deviation into account? i.e. if your finger goes slightly back in Recognizing state, won't that mean rejecting the gesture altogether?

=====

 744 + recognition will fail. It will also fail it the drag or flick is too short. E.g. a

→ "...fail if the drag..."

=====

 740 +/*
 741 + An area that detects axis-aligned single-finger drag gestures

could we ask for a little more documentation on the properties?

=====

 755 + // stuff that will be set in stone at some point

as discussed, I do agree we need strong defaults here, but allowing advanced use where those would be modifiable could be possible as well

=====

 768 + bool positiveDirection() const { return m_positiveDirection; }

 773 + qreal maxDeviation() const {return m_maxDeviation;}

 776 + qreal wideningAngle() const {return m_wideningAngle;}

 779 + qreal distanceThreshold() const {return m_distanceThreshold;}

please use spaces after/before curly braces consistently

=====

 764 + enum Orientation { Horizontal = Qt::Horizontal, Vertical = Qt::Vertical };

 812 + enum {

please consider using strongly-typed enums (although I don't think we've c...

Read more...

review: Needs Fixing
Revision history for this message
Michał Sawicz (saviq) wrote :

One more thing:

972 +add_subdirectory(UbuntuGestures)
973
974 === added directory 'tests/plugins/UbuntuGestures'

Please mimic the same directory structure as the source - so 'tests/plugins/Ubuntu/Gestures'

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> hmm... any way to make the example work with a pointer? can we enable pointer
> composition?

I'm afraid we will have to add code that manually synthesizes QTouchEvents out of QMouseEvents as Qt::AA_SynthesizeTouchForUnhandledMouseEvents might not be exactly what we need.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

W dniu 14.05.2013 14:58, Daniel d'Andrada pisze:
>> hmm... any way to make the example work with a pointer? can we enable pointer
>> > composition?
> I'm afraid we will have to add code that manually synthesizes QTouchEvents out of QMouseEvents as Qt::AA_SynthesizeTouchForUnhandledMouseEvents might not be exactly what we need.

Why is that? If I read [1] correctly that should work? Although the
"Unhandled" part might be tricky...

[1]
http://qt-project.org/doc/qt-5.0/qtcore/qt.html#ApplicationAttribute-enum
--
Michał Sawicz <email address hidden>
Canonical Services Ltd.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> One more thing:
>
> 972 +add_subdirectory(UbuntuGestures)
> 973
> 974 === added directory 'tests/plugins/UbuntuGestures'
>
> Please mimic the same directory structure as the source - so
> 'tests/plugins/Ubuntu/Gestures'

Done.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :
Download full text (7.1 KiB)

> * instead of the pair direction, positiveDirection, I'd go for a single enum
> with 4 values, what do you think?

Done.

> * we talked yesterday about the possibility to add a time threshold as well
> (within which the touch must cross the distance threshold), do you think you
> could add it here or in a later change?

I can add it in a later change.

> * there seems to be a bunch of things common with the QtWidgets
> QGestureRecognizer (state names, for example) [1] - do you think it would make
> sense to converge on the same terminology?

You mean those signals?

    void dragStarted();
    void directionalDragRecognized();
    void dragRejected();
    void dragEnded();

Compared to this enum?

    enum ResultFlag { Ignore, MayBeGesture, TriggerGesture, FinishGesture, CancelGesture, ConsumeEventHint }

Well, they are intented for different audiences (the former for application code and the latter for the gesture fw engine) in a bit different contexts. I preffer the signal naming the way they are, as they look very clear in meaning IMHO. One thing I see could change though is s/dragRejected/dragCancelled

> * could you add some newlines to tests/plugins/UbuntuGestures/CMakeLists.txt
> for readability?

Done.

>
> =====
>
> 360 +add_definitions(-DUBUNTUGESTURES_LIBRARY)
> 361 +
> 362 +add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES})
>
> 749 +class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {
>
> 856 +#if defined(UBUNTUGESTURES_LIBRARY)
> 857 +# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT
> 858 +#else
> 859 +# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT
> 860 +#endif
>
> 973 + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
>
> 982 +target_link_libraries(DirectionalDragAreaTestExec UbuntuGestureQml)
>
> maybe directly use the .cpp in test sources instead? We don't really want it
> to be a library, do we.

Why not? Makes testing simpler. Recompiling the whole plugin in the test directory seems wasteful.

>
> =====
>
> 538 + if (touchPoint->state() == Qt::TouchPointReleased) {
>
> is it not possible here for touchPoint to be invalid?

No. A point has to be released before it disappears from the list of active points.

> why wouldn't QTouchEvent::touchPoints() be enough here?

We are tracking a specific touch point. Further touch points might come in after the gesture is recgonized but we just don't care.

>
> =====
>
> I'm wondering if the "pointInside..." calculations couldn't be simplified by
> using a qreal factor instead of the angle - you'd just need to make sure that:
>
> -d <= Δy <= Δx * f + d
>
> where y is perpendicular to the gesture direction (horizontal positive / left-
> to-right in that case), d is max deviation, f is the factor; with f = 1 that
> would mean 45° widening angle; what do you think?

I think you mean:
Δx >= -d && abs(Δy) <= (Δx * f) + d

Yes, we can skip use of trigonometric functions by using a widening factor parameter instead of a widening angle.
Done.

>
> =====
>
> 666 +bool DirectionalDragArea::movingInRightDirection(const QPointF &point)
> 667 +{
> 668 + if (m_orientation == Horizontal) {
> 669 + if (m_positiveDi...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :
Download full text (3.3 KiB)

W dniu 16.05.2013 01:04, Daniel d'Andrada pisze:
>> * there seems to be a bunch of things common with the QtWidgets
>> QGestureRecognizer (state names, for example) [1] - do you think it would make
>> sense to converge on the same terminology?
>
> You mean those signals?
>
> void dragStarted();
> void directionalDragRecognized();
> void dragRejected();
> void dragEnded();
>
> Compared to this enum?
>
> enum ResultFlag { Ignore, MayBeGesture, TriggerGesture, FinishGesture, CancelGesture, ConsumeEventHint }
>
> Well, they are intented for different audiences (the former for application code and the latter for the gesture fw engine) in a bit different contexts. I preffer the signal naming the way they are, as they look very clear in meaning IMHO. One thing I see could change though is s/dragRejected/dragCancelled

Your call.

>> =====
>>
>> 360 +add_definitions(-DUBUNTUGESTURES_LIBRARY)
>> 361 +
>> 362 +add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES})
>>
>> 749 +class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {
>>
>> 856 +#if defined(UBUNTUGESTURES_LIBRARY)
>> 857 +# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT
>> 858 +#else
>> 859 +# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT
>> 860 +#endif
>>
>> 973 + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
>>
>> 982 +target_link_libraries(DirectionalDragAreaTestExec UbuntuGestureQml)
>>
>> maybe directly use the .cpp in test sources instead? We don't really want it
>> to be a library, do we.
>
> Why not? Makes testing simpler. Recompiling the whole plugin in the test directory seems wasteful.

There's just a lot here that suggests this is meant to be used as a
shared library, which it isn't. But I can live with that. We should
probably simply find a way to use the plugins from C++.

>> 764 + enum Orientation { Horizontal = Qt::Horizontal, Vertical =
>> Qt::Vertical };
>>
>> 812 + enum {
>>
>> please consider using strongly-typed enums (although I don't think we've
>> checked how QML currently deals with passing them to/from QML - so we might
>> need to wait there)
>
> Yeah, I think we might need to wait indeed.

I've used them successfully in [1] - they end up as numbers in QML, so
there's some casting involved, but they seem to work fine.

>> do you have a Nexus 10 to try it out? I'm getting quite some false negatives
>> with the examples
>
> No, I don't. Do you have a Galaxy Nexus to try it out and see if results differ? That's the device I have.
>
> It seems that drags coming from the top, left and right borders are being filtered out by mir as those borders are reserved for the shell and qmlscene is not the shell? Because no input events come at all in those situations. If you start dragging already from withing the window it works.
> Also from the bottom border it always works, I guess it's because the bottom edge is for application's tool bars.

You can stop qml-phone-shell by removing its line from
/etc/phone-services on the device and `sudo restart ubuntu-session`.
You'll get all the input then.

>> also, tapping in the edge area results in the red rectangle to stay on screen
>
> Details, deta...

Read more...

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> > It seems that drags coming from the top, left and right borders are being
> filtered out by mir as those borders are reserved for the shell and qmlscene
> is not the shell? Because no input events come at all in those situations. If
> you start dragging already from withing the window it works.
> > Also from the bottom border it always works, I guess it's because the bottom
> edge is for application's tool bars.
>
> You can stop qml-phone-shell by removing its line from
> /etc/phone-services on the device and `sudo restart ubuntu-session`.
> You'll get all the input then.
>

qml-phone-shell wasn't running while I was trying out the example/test app on the device.

Revision history for this message
Michał Sawicz (saviq) wrote :

1087 + COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/*.qml" ${CMAKE_CURRENT_BINARY_DIR}

This should use ${qmlFiles}, too.

review: Needs Fixing
Revision history for this message
Michał Sawicz (saviq) wrote :

522 +qreal DirectionalDragArea::wideningAngle() const
523 +{
524 + // convert factor (which is in fact a tangent value) to its corresponding
525 + // angle in degrees
526 + return qAtan(m_wideningFactor) * 180.0 / M_PI;
527 +}
528 +
529 +void DirectionalDragArea::setWideningAngle(qreal angle)
530 +{
531 + qreal newFactor = qTan(angle * M_PI / 180.0);
532 +
533 + if (m_wideningFactor != newFactor) {
534 + m_wideningFactor = newFactor;
535 + Q_EMIT wideningAngleChanged(angle);
536 + }
537 +}

Depending on rounding errors, wideningAngle() might return a different value than was set (and emitted).

Shouldn't this be int now that we're doing degrees?

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 522 +qreal DirectionalDragArea::wideningAngle() const
> 523 +{
> 524 + // convert factor (which is in fact a tangent value) to its
> corresponding
> 525 + // angle in degrees
> 526 + return qAtan(m_wideningFactor) * 180.0 / M_PI;
> 527 +}
> 528 +
> 529 +void DirectionalDragArea::setWideningAngle(qreal angle)
> 530 +{
> 531 + qreal newFactor = qTan(angle * M_PI / 180.0);
> 532 +
> 533 + if (m_wideningFactor != newFactor) {
> 534 + m_wideningFactor = newFactor;
> 535 + Q_EMIT wideningAngleChanged(angle);
> 536 + }
> 537 +}
>
> Depending on rounding errors, wideningAngle() might return a different value
> than was set (and emitted).

Fixed.

> Shouldn't this be int now that we're doing degrees?

No. Nothing stops you from having widening angles such as 9.36 degrees

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 1087 + COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/*.qml"
> ${CMAKE_CURRENT_BINARY_DIR}
>
> This should use ${qmlFiles}, too.

Done.

Revision history for this message
Michał Sawicz (saviq) wrote :

694 + case Upwards:
695 + return dY <= m_maxDeviation && qFabs(dX) <= (m_maxDeviation + dY) * m_wideningFactor + m_maxDeviation;
696 + case Downwards:
697 + return dY >= -m_maxDeviation && qFabs(dX) <= (m_maxDeviation + dY) * m_wideningFactor + m_maxDeviation;
698 + case Leftwards:
699 + return dX <= m_maxDeviation && qFabs(dY) <= (m_maxDeviation + dX) * m_wideningFactor + m_maxDeviation;
700 + default: // Rightwards:
701 + return dX >= -m_maxDeviation && qFabs(dY) <= (m_maxDeviation + dX) * m_wideningFactor + m_maxDeviation;

Why adding maxDeviation before multiplying by the factor?

review: Needs Information
Revision history for this message
Michał Sawicz (saviq) wrote :

W dniu 16.05.2013 15:52, Daniel d'Andrada pisze:
> No. Nothing stops you from having widening angles such as 9.36 degrees

But we can ;)

--
Michał Sawicz <email address hidden>
Canonical Services Ltd.

Revision history for this message
Michał Sawicz (saviq) wrote :

I'll have a fresh overview look tomorrow morning, but this looks good!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 694 + case Upwards:
> 695 + return dY <= m_maxDeviation && qFabs(dX) <= (m_maxDeviation + dY) *
> m_wideningFactor + m_maxDeviation;
> 696 + case Downwards:
> 697 + return dY >= -m_maxDeviation && qFabs(dX) <= (m_maxDeviation + dY) *
> m_wideningFactor + m_maxDeviation;
> 698 + case Leftwards:
> 699 + return dX <= m_maxDeviation && qFabs(dY) <= (m_maxDeviation + dX) *
> m_wideningFactor + m_maxDeviation;
> 700 + default: // Rightwards:
> 701 + return dX >= -m_maxDeviation && qFabs(dY) <= (m_maxDeviation + dX) *
> m_wideningFactor + m_maxDeviation;
>
> Why adding maxDeviation before multiplying by the factor?

Because the widening starts from startPos - maxDeviation, not from startPos itself (see doc/DirectionalDragArea.svg)

Revision history for this message
Michał Sawicz (saviq) wrote :
Download full text (3.8 KiB)

 803 +#include <QQuickItem>

1025 +#include <QQmlExtensionPlugin>

1513 +#include <QtTest/QtTest>
1514 +#include <QObject>
1515 +#include <qpa/qwindowsysteminterface.h>
1516 +#include <QQuickView>
1517 +#include <QQmlEngine>

Please use <QtQuick/QQuickItem> etc.

=====

Damper.h should #include <QtCore/QPointF>.

=====

 407 + void setMaxDelta(Type maxDelta) {
 408 + m_maxDelta = maxDelta;
 409 + }

 418 + if (delta > 0 && delta > m_maxDelta) {
 419 + m_value += delta - m_maxDelta;
 420 + } else if (delta < 0 && delta < -m_maxDelta) {
 421 + m_value += delta - m_maxDelta;
 422 + }

The > 0, < 0 seem unnecessary? Although setMaxDelta() should make sure that it's > 0?

=====

 434 +/*
 435 + A point that has its movement dampened.
 436 + */
 437 +class DampedPointF {
 438 +public:
 439 + void setMaxDelta(qreal maxDelta) {
 440 + m_x.setMaxDelta(maxDelta);
 441 + m_y.setMaxDelta(maxDelta);
 442 + }
 443 +
 444 + qreal maxDelta() const { return m_x.maxDelta(); }
 445 +
 446 + void reset(const QPointF &point) {
 447 + m_x.reset(point.x());
 448 + m_y.reset(point.y());
 449 + }
 450 +
 451 + void update(const QPointF &point) {
 452 + m_x.update(point.x());
 453 + m_y.update(point.y());
 454 + }
 455 +
 456 + qreal x() const { return m_x.value(); }
 457 + qreal y() const { return m_y.value(); }
 458 +private:
 459 + Damper<qreal> m_x;
 460 + Damper<qreal> m_y;
 461 +};

I know it's probably minor (square vs. circular dampening area), but shouldn't we use QPointF::operator- and QPointF::manhattanLength here?

=====

 561 + if (m_state == Rejected)
 562 + return 0.0;

Please wrap all blocks in braces.

=====

 633 + if (!pointInsideAllowedArea(touchPos)) {
 634 + setState(Rejected);
 635 + return;
 636 + }
 637 +
 638 + m_previousDampedPos.setX(m_dampedPos.x());
 639 + m_previousDampedPos.setY(m_dampedPos.y());
 640 + m_dampedPos.update(touchPos);
 641 +
 642 + if (!movingInRightDirection()) {
 643 + setState(Rejected);
 644 + return;
 645 + }

 704 +bool DirectionalDragArea::movingInRightDirection() const
 705 +{
 706 + switch (m_direction) {
 707 + case Upwards:
 708 + return m_dampedPos.y() <= m_previousDampedPos.y();
 709 + case Downwards:
 710 + return m_dampedPos.y() >= m_previousDampedPos.y();
 711 + case Leftwards:
 712 + return m_dampedPos.x() <= m_previousDampedPos.x();
 713 + default: // Rightwards:
 714 + return m_dampedPos.x() >= m_previousDampedPos.x();
 715 + }
 716 +}

I would think I should be able to move within maxDeviation from the start point in Recognizing state and that would not cause a rejection. But if I'm reading the above correctly, I'm only really allowed to move within movementDamperMaxDelta from the start position, no?

=====

 851 + Q_PROPERTY(qreal movementDamperMaxDelta
 852 + READ movementDamperMaxDelta
 853 + WRITE setMovementDamperMaxDelta
 854 + NOTIFY movementDamperMaxDeltaChanged)

I'd probably go for ...

Read more...

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :
Download full text (5.2 KiB)

> 803 +#include <QQuickItem>
>
> 1025 +#include <QQmlExtensionPlugin>
>
> 1513 +#include <QtTest/QtTest>
> 1514 +#include <QObject>
> 1515 +#include <qpa/qwindowsysteminterface.h>
> 1516 +#include <QQuickView>
> 1517 +#include <QQmlEngine>
>
> Please use <QtQuick/QQuickItem> etc.

Done.

>
> =====
>
> Damper.h should #include <QtCore/QPointF>.

Done.

>
> =====
>
> 407 + void setMaxDelta(Type maxDelta) {
> 408 + m_maxDelta = maxDelta;
> 409 + }
>
> 418 + if (delta > 0 && delta > m_maxDelta) {
> 419 + m_value += delta - m_maxDelta;
> 420 + } else if (delta < 0 && delta < -m_maxDelta) {
> 421 + m_value += delta - m_maxDelta;
> 422 + }
>
> The > 0, < 0 seem unnecessary?

No, it's necessary. I do have to distinguish between a negative and a positive delta. Speaking of which, I just spotted a bug in diff line 421. It should be "m_value += delta + m_maxDelta;" instead. Fixed it.

> Although setMaxDelta() should make sure that
> it's > 0?

Yeah, added the check.

>
> =====
>
> 434 +/*
> 435 + A point that has its movement dampened.
> 436 + */
> 437 +class DampedPointF {
> 438 +public:
> 439 + void setMaxDelta(qreal maxDelta) {
> 440 + m_x.setMaxDelta(maxDelta);
> 441 + m_y.setMaxDelta(maxDelta);
> 442 + }
> 443 +
> 444 + qreal maxDelta() const { return m_x.maxDelta(); }
> 445 +
> 446 + void reset(const QPointF &point) {
> 447 + m_x.reset(point.x());
> 448 + m_y.reset(point.y());
> 449 + }
> 450 +
> 451 + void update(const QPointF &point) {
> 452 + m_x.update(point.x());
> 453 + m_y.update(point.y());
> 454 + }
> 455 +
> 456 + qreal x() const { return m_x.value(); }
> 457 + qreal y() const { return m_y.value(); }
> 458 +private:
> 459 + Damper<qreal> m_x;
> 460 + Damper<qreal> m_y;
> 461 +};
>
> I know it's probably minor (square vs. circular dampening area), but shouldn't
> we use QPointF::operator- and QPointF::manhattanLength here?

Using a circular area is computationally more expensive and we're just fine with this less accurate method.

Using a manhattan lenght would result in an area defined by a square rotated by 45 degrees instead of an axis-aligned square as it is now. So, in the end, these two approaches are equivalent (a square area).

>
> =====
>
> 561 + if (m_state == Rejected)
> 562 + return 0.0;
>
> Please wrap all blocks in braces.

Done.

>
> =====
>
> 633 + if (!pointInsideAllowedArea(touchPos)) {
> 634 + setState(Rejected);
> 635 + return;
> 636 + }
> 637 +
> 638 + m_previousDampedPos.setX(m_dampedPos.x());
> 639 + m_previousDampedPos.setY(m_dampedPos.y());
> 640 + m_dampedPos.update(touchPos);
> 641 +
> 642 + if (!movingInRightDirection()) {
> 643 + setState(Rejected);
> 644 + return;
> 645 + }
>
> 704 +bool DirectionalDragArea::movingInRightDirection() const
> 705 +{
> 706 + switch (m_direction) {
> 707 + case Upwards:
> 708 + return m_dampedPos.y() <= m_previousDampedPos.y();
> 709 + case Downwards:
> 710 + return m_dampedPo...

Read more...

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Ah, and thanks for the thorough reviews. Code is getting better and better!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

W dniu 17.05.2013 20:37, Daniel d'Andrada pisze:
>> I wonder if DirectionalDragArea::state (or status to avoid confusion) should
>> be exposed to QML? Or maybe a "dragging" boolean?
>
> Yeah, we could expose the state variable indeed.
> Done.

818 + // The current state of the directional drag gesture.
819 + Q_PROPERTY(GestureState gestureState READ gestureState NOTIFY
gestureStateChanged)

Could we go with "status" to be consistent with other QML components?

And I would still like a boolean "dragging" more in favor of dragEnded,
dragStarted and dragRecognized signals.

>> Shouldn't dragValue be reset to 0 when going back to WaitingForTouch? Or maybe
>> that's a higher level's responsibility to make sure of that...
>
> No, I think it should stay as it is. It's up to the application to decide what to do once the gesture ends.

731 + default: // Rejected
732 + Q_EMIT dragRejected();
733 + Q_EMIT distanceChanged(distance());

Shouldn't that not emit the distance then? I'm just worried that people
might rely on it to cancel the movement, but then the dragRejected
signal (or draggingChanged(false) when you introduce that property)
should already be enough to say "the gesture is uninteresting, do what
you need to cancel".

>> 1070 + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
>>
>> This should be set as an ENVIRONMENT property on the test instead.
>
> Why? So you want CMake to pass it as a DEFINE to tst_DirectionalDragArea.cpp during compilation?

No, I mean it should be possible to use set_target_properties() to pass
that environment var to the target, just as we do in add_qml_test() to
set the QPA platform to "minimal".

--
Michał Sawicz <email address hidden>
Canonical Services Ltd.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 818 + // The current state of the directional drag gesture.
> 819 + Q_PROPERTY(GestureState gestureState READ gestureState NOTIFY
> gestureStateChanged)
>
> Could we go with "status" to be consistent with other QML components?

Done.

>
> And I would still like a boolean "dragging" more in favor of dragEnded,
> dragStarted and dragRecognized signals.

Done.

>
> >> Shouldn't dragValue be reset to 0 when going back to WaitingForTouch? Or
> maybe
> >> that's a higher level's responsibility to make sure of that...
> >
> > No, I think it should stay as it is. It's up to the application to decide
> what to do once the gesture ends.
>
> 731 + default: // Rejected
> 732 + Q_EMIT dragRejected();
> 733 + Q_EMIT distanceChanged(distance());
>
> Shouldn't that not emit the distance then? I'm just worried that people
> might rely on it to cancel the movement, but then the dragRejected
> signal (or draggingChanged(false) when you introduce that property)
> should already be enough to say "the gesture is uninteresting, do what
> you need to cancel".

For the sake of consistency with the transition to WaitingForTouch, I made it keep its last valid value while on Rejected state.

>
> >> 1070 + LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
> >>
> >> This should be set as an ENVIRONMENT property on the test instead.
> >
> > Why? So you want CMake to pass it as a DEFINE to tst_DirectionalDragArea.cpp
> during compilation?
>
> No, I mean it should be possible to use set_target_properties() to pass
> that environment var to the target, just as we do in add_qml_test() to
> set the QPA platform to "minimal".

The test for DirectionalDragArea is a custom target (using add_custom_target()) not a proper cmake test (using add_test()). A custom target doesn't have (or use) an ENVIRONMENT property.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

889 + bool dragging() const { return m_status && (Undecided || Recognized); }

This looks incorrect. Undecided || Recognized is always true (assuming their values are 1 and 2, respectively) so dragging becomes (bool)m_status, which will, only be false in WaitingForTouch status.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 889 + bool dragging() const { return m_status && (Undecided ||
> Recognized); }
>
> This looks incorrect. Undecided || Recognized is always true (assuming their
> values are 1 and 2, respectively) so dragging becomes (bool)m_status, which
> will, only be false in WaitingForTouch status.

Right, they're not flags. Silly mistake. Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/qml-phone-shell.install'
2--- debian/qml-phone-shell.install 2013-05-16 12:02:23 +0000
3+++ debian/qml-phone-shell.install 2013-05-20 13:14:26 +0000
4@@ -16,3 +16,4 @@
5 /usr/share/qml-phone-shell/plugins/LightDM/*
6 /usr/share/qml-phone-shell/plugins/Unity/*
7 /usr/share/qml-phone-shell/plugins/Utils/*
8+/usr/share/qml-phone-shell/plugins/Ubuntu/*
9
10=== added file 'doc/DirectionalDragArea.svg'
11--- doc/DirectionalDragArea.svg 1970-01-01 00:00:00 +0000
12+++ doc/DirectionalDragArea.svg 2013-05-20 13:14:26 +0000
13@@ -0,0 +1,311 @@
14+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
15+<!-- Created with Inkscape (http://www.inkscape.org/) -->
16+
17+<svg
18+ xmlns:dc="http://purl.org/dc/elements/1.1/"
19+ xmlns:cc="http://creativecommons.org/ns#"
20+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
21+ xmlns:svg="http://www.w3.org/2000/svg"
22+ xmlns="http://www.w3.org/2000/svg"
23+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
24+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
25+ width="644.35803"
26+ height="626.32886"
27+ id="svg2"
28+ version="1.1"
29+ inkscape:version="0.48.4 r9939"
30+ sodipodi:docname="DirectionalDragArea.svg">
31+ <defs
32+ id="defs4">
33+ <linearGradient
34+ id="linearGradient6853">
35+ <stop
36+ style="stop-color:#00adff;stop-opacity:1;"
37+ offset="0"
38+ id="stop6855" />
39+ <stop
40+ id="stop6861"
41+ offset="0.82191783"
42+ style="stop-color:#00adff;stop-opacity:0.49803922;" />
43+ <stop
44+ style="stop-color:#000000;stop-opacity:0;"
45+ offset="1"
46+ id="stop6857" />
47+ </linearGradient>
48+ <marker
49+ inkscape:stockid="Arrow1Send"
50+ orient="auto"
51+ refY="0"
52+ refX="0"
53+ id="Arrow1Send"
54+ style="overflow:visible">
55+ <path
56+ id="path4958"
57+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
58+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
59+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
60+ inkscape:connector-curvature="0" />
61+ </marker>
62+ <marker
63+ inkscape:stockid="Arrow1Sstart"
64+ orient="auto"
65+ refY="0"
66+ refX="0"
67+ id="Arrow1Sstart"
68+ style="overflow:visible">
69+ <path
70+ id="path4955"
71+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
72+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
73+ transform="matrix(0.2,0,0,0.2,1.2,0)"
74+ inkscape:connector-curvature="0" />
75+ </marker>
76+ <marker
77+ inkscape:stockid="Arrow1Mstart"
78+ orient="auto"
79+ refY="0"
80+ refX="0"
81+ id="Arrow1Mstart"
82+ style="overflow:visible">
83+ <path
84+ id="path4949"
85+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
86+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
87+ transform="matrix(0.4,0,0,0.4,4,0)"
88+ inkscape:connector-curvature="0" />
89+ </marker>
90+ <marker
91+ inkscape:stockid="Arrow1Sstart"
92+ orient="auto"
93+ refY="0"
94+ refX="0"
95+ id="Arrow1Sstart-4"
96+ style="overflow:visible">
97+ <path
98+ inkscape:connector-curvature="0"
99+ id="path4955-5"
100+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
101+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
102+ transform="matrix(0.2,0,0,0.2,1.2,0)" />
103+ </marker>
104+ <marker
105+ inkscape:stockid="Arrow1Send"
106+ orient="auto"
107+ refY="0"
108+ refX="0"
109+ id="Arrow1Send-4"
110+ style="overflow:visible">
111+ <path
112+ inkscape:connector-curvature="0"
113+ id="path4958-6"
114+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
115+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
116+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
117+ </marker>
118+ </defs>
119+ <sodipodi:namedview
120+ id="base"
121+ pagecolor="#ffffff"
122+ bordercolor="#666666"
123+ borderopacity="1.0"
124+ inkscape:pageopacity="1"
125+ inkscape:pageshadow="2"
126+ inkscape:zoom="1.4158879"
127+ inkscape:cx="311.5"
128+ inkscape:cy="313"
129+ inkscape:document-units="px"
130+ inkscape:current-layer="layer1"
131+ showgrid="false"
132+ showguides="true"
133+ inkscape:guide-bbox="true"
134+ fit-margin-top="5"
135+ fit-margin-left="5"
136+ fit-margin-right="5"
137+ fit-margin-bottom="5"
138+ inkscape:window-width="1920"
139+ inkscape:window-height="1056"
140+ inkscape:window-x="0"
141+ inkscape:window-y="24"
142+ inkscape:window-maximized="1" />
143+ <metadata
144+ id="metadata7">
145+ <rdf:RDF>
146+ <cc:Work
147+ rdf:about="">
148+ <dc:format>image/svg+xml</dc:format>
149+ <dc:type
150+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
151+ <dc:title />
152+ </cc:Work>
153+ </rdf:RDF>
154+ </metadata>
155+ <g
156+ inkscape:label="Layer 1"
157+ inkscape:groupmode="layer"
158+ id="layer1"
159+ transform="translate(-253.65772,218.97803)">
160+ <path
161+ style="fill:#00adff;fill-opacity:0.75686275;stroke:none"
162+ d="M 259.64804,57.193502 792.57928,-142.14719 793.2573,341.96589 259.60777,174.1189 z"
163+ id="path6851"
164+ inkscape:connector-curvature="0"
165+ sodipodi:nodetypes="ccccc" />
166+ <path
167+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
168+ d="m 890.84952,-178.96696 -631.14314,236.973167 0,116.242063 630.29325,197.35579"
169+ id="path4887"
170+ inkscape:connector-curvature="0"
171+ sodipodi:nodetypes="cccc" />
172+ <path
173+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:12.5458998, 12.5458998;stroke-dashoffset:0"
174+ d="m 260.83781,59.275793 630.97079,0"
175+ id="path4891"
176+ inkscape:connector-curvature="0" />
177+ <path
178+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:12.5458998, 12.5458998;stroke-dashoffset:0"
179+ d="m 260.99944,172.08634 630.97079,0"
180+ id="path4891-7"
181+ inkscape:connector-curvature="0" />
182+ <path
183+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:8.3639332, 8.3639332;stroke-dashoffset:0"
184+ d="m 793.23579,-189.85001 0,591.15535"
185+ id="path4911"
186+ inkscape:connector-curvature="0"
187+ sodipodi:nodetypes="cc" />
188+ <path
189+ sodipodi:type="arc"
190+ style="fill:none;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
191+ id="path4915"
192+ sodipodi:cx="259.74588"
193+ sodipodi:cy="211.80014"
194+ sodipodi:rx="18.943148"
195+ sodipodi:ry="18.943148"
196+ d="m 277.40703,204.94983 a 18.943148,18.943148 0 0 1 1.28197,6.81725"
197+ transform="matrix(2.987119,0,0,2.987119,-515.9493,-573.75675)"
198+ sodipodi:open="true"
199+ sodipodi:start="5.9131755"
200+ sodipodi:end="6.28144" />
201+ <path
202+ sodipodi:type="arc"
203+ style="fill:none;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
204+ id="path4915-5"
205+ sodipodi:cx="259.74588"
206+ sodipodi:cy="211.80014"
207+ sodipodi:rx="18.943148"
208+ sodipodi:ry="18.943148"
209+ d="m 278.67886,211.17971 a 18.943148,18.943148 0 0 1 -0.8432,6.24205"
210+ transform="matrix(2.987119,0,0,2.987119,-517.22894,-458.90756)"
211+ sodipodi:start="6.250427"
212+ sodipodi:end="6.5844864"
213+ sodipodi:open="true" />
214+ <path
215+ sodipodi:type="arc"
216+ style="fill:#000000;fill-opacity:1;stroke:none"
217+ id="path4935"
218+ sodipodi:cx="282.05548"
219+ sodipodi:cy="227.78796"
220+ sodipodi:rx="1.00761"
221+ sodipodi:ry="1.00761"
222+ d="m 283.06309,227.78796 a 1.00761,1.00761 0 1 1 -2.01522,0 1.00761,1.00761 0 1 1 2.01522,0 z"
223+ transform="matrix(2.987119,0,0,2.987119,-525.77343,-564.0127)" />
224+ <path
225+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Sstart);marker-end:url(#Arrow1Send)"
226+ d="m 317.09757,65.254063 0,42.548767"
227+ id="path4937"
228+ inkscape:connector-curvature="0" />
229+ <path
230+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Sstart);marker-end:url(#Arrow1Send)"
231+ d="m 308.28828,116.39919 -42.54876,0"
232+ id="path4937-9"
233+ inkscape:connector-curvature="0" />
234+ <text
235+ xml:space="preserve"
236+ style="font-size:29.87118912px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
237+ x="321.45822"
238+ y="92.085648"
239+ id="text5787"
240+ sodipodi:linespacing="125%"><tspan
241+ sodipodi:role="line"
242+ id="tspan5789"
243+ x="321.45822"
244+ y="92.085648"
245+ style="font-size:11.94847584px">maxDeviation</tspan></text>
246+ <text
247+ xml:space="preserve"
248+ style="font-size:29.87118912px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
249+ x="322.99289"
250+ y="48.58828"
251+ id="text5787-2"
252+ sodipodi:linespacing="125%"><tspan
253+ sodipodi:role="line"
254+ id="tspan5789-5"
255+ x="322.99289"
256+ y="48.58828"
257+ style="font-size:11.94847584px">wideningAngle</tspan></text>
258+ <path
259+ inkscape:connector-curvature="0"
260+ id="path5830"
261+ d="m 315.70321,-189.85001 0,225.307165"
262+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:8.3639332, 8.3639332;stroke-dashoffset:0"
263+ sodipodi:nodetypes="cc" />
264+ <path
265+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
266+ d="m 315.79005,-204.37393 476.77774,0"
267+ id="path5832"
268+ inkscape:connector-curvature="0"
269+ sodipodi:nodetypes="cc" />
270+ <path
271+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
272+ d="m 315.303,-212.93254 0,17.24097"
273+ id="path6839"
274+ inkscape:connector-curvature="0" />
275+ <path
276+ inkscape:connector-curvature="0"
277+ id="path6841"
278+ d="m 792.91626,-212.93254 0,17.24097"
279+ style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
280+ <text
281+ sodipodi:linespacing="125%"
282+ id="text6843"
283+ y="-188.72206"
284+ x="483.68591"
285+ style="font-size:29.87118912px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
286+ xml:space="preserve"><tspan
287+ style="font-size:11.94847584px"
288+ y="-188.72206"
289+ x="483.68591"
290+ id="tspan6845"
291+ sodipodi:role="line">distanceThreshold</tspan></text>
292+ <text
293+ sodipodi:linespacing="125%"
294+ id="text6847"
295+ y="121.95684"
296+ x="321.45822"
297+ style="font-size:29.87118912px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
298+ xml:space="preserve"><tspan
299+ style="font-size:11.94847584px"
300+ y="121.95684"
301+ x="321.45822"
302+ id="tspan6849"
303+ sodipodi:role="line">Position of touch press</tspan></text>
304+ <rect
305+ style="fill:#00adff;fill-opacity:0.75686275;stroke:none"
306+ id="rect6863"
307+ width="35.257538"
308+ height="35.935566"
309+ x="295.58365"
310+ y="318.91293" />
311+ <text
312+ xml:space="preserve"
313+ style="font-size:29.87118912px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
314+ x="335.01886"
315+ y="340.96045"
316+ id="text6865"
317+ sodipodi:linespacing="125%"><tspan
318+ sodipodi:role="line"
319+ id="tspan6867"
320+ x="335.01886"
321+ y="340.96045"
322+ style="font-size:11.94847584px">Valid area for touch position during recognition</tspan></text>
323+ </g>
324+</svg>
325
326=== modified file 'plugins/CMakeLists.txt'
327--- plugins/CMakeLists.txt 2013-04-19 15:06:41 +0000
328+++ plugins/CMakeLists.txt 2013-05-20 13:14:26 +0000
329@@ -1,3 +1,4 @@
330+add_subdirectory(Ubuntu)
331 add_subdirectory(Utils)
332 add_subdirectory(Unity)
333 add_subdirectory(HudClient)
334
335=== added directory 'plugins/Ubuntu'
336=== added file 'plugins/Ubuntu/CMakeLists.txt'
337--- plugins/Ubuntu/CMakeLists.txt 1970-01-01 00:00:00 +0000
338+++ plugins/Ubuntu/CMakeLists.txt 2013-05-20 13:14:26 +0000
339@@ -0,0 +1,1 @@
340+add_subdirectory(Gestures)
341
342=== added directory 'plugins/Ubuntu/Gestures'
343=== added file 'plugins/Ubuntu/Gestures/CMakeLists.txt'
344--- plugins/Ubuntu/Gestures/CMakeLists.txt 1970-01-01 00:00:00 +0000
345+++ plugins/Ubuntu/Gestures/CMakeLists.txt 2013-05-20 13:14:26 +0000
346@@ -0,0 +1,30 @@
347+set(CMAKE_AUTOMOC ON)
348+
349+include(FindPkgConfig)
350+find_package(Qt5Core REQUIRED)
351+find_package(Qt5Quick REQUIRED)
352+
353+set(UbuntuGestureQml_SOURCES
354+ plugin.cpp
355+ DirectionalDragArea.cpp
356+)
357+
358+add_definitions(-DUBUNTUGESTURES_LIBRARY)
359+
360+add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES})
361+
362+qt5_use_modules(UbuntuGestureQml Core Quick)
363+
364+# copy files into build directory for shadow builds
365+add_custom_target(UbuntuGestureQmlDirFile ALL
366+ COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
367+ DEPENDS qmldir
368+)
369+
370+install(TARGETS UbuntuGestureQml
371+ DESTINATION ${SHELL_APP_DIR}/plugins/Ubuntu/Gestures
372+ )
373+
374+install(FILES qmldir
375+ DESTINATION ${SHELL_APP_DIR}/plugins/Ubuntu/Gestures
376+ )
377
378=== added file 'plugins/Ubuntu/Gestures/Damper.h'
379--- plugins/Ubuntu/Gestures/Damper.h 1970-01-01 00:00:00 +0000
380+++ plugins/Ubuntu/Gestures/Damper.h 2013-05-20 13:14:26 +0000
381@@ -0,0 +1,85 @@
382+/*
383+ * Copyright (C) 2013 Canonical, Ltd.
384+ *
385+ * This program is free software; you can redistribute it and/or modify
386+ * it under the terms of the GNU General Public License as published by
387+ * the Free Software Foundation; version 3.
388+ *
389+ * This program is distributed in the hope that it will be useful,
390+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
391+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
392+ * GNU General Public License for more details.
393+ *
394+ * You should have received a copy of the GNU General Public License
395+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
396+ */
397+
398+#ifndef UBUNTU_GESTURES_DAMPER_H
399+#define UBUNTU_GESTURES_DAMPER_H
400+
401+#include <QtCore/QPointF>
402+
403+/*
404+ Decreases the oscillations of a value along an axis.
405+ */
406+template <class Type> class Damper {
407+public:
408+ // Maximum delta between the raw value and its dampened counterpart.
409+ void setMaxDelta(Type maxDelta) {
410+ if (maxDelta < 0) qFatal("Damper::maxDelta must be a positive number.");
411+ m_maxDelta = maxDelta;
412+ }
413+ Type maxDelta() const { return m_maxDelta; }
414+
415+ void reset(Type value) {
416+ m_value = value;
417+ }
418+
419+ Type update(Type value) {
420+ Type delta = value - m_value;
421+ if (delta > 0 && delta > m_maxDelta) {
422+ m_value += delta - m_maxDelta;
423+ } else if (delta < 0 && delta < -m_maxDelta) {
424+ m_value += delta + m_maxDelta;
425+ }
426+
427+ return m_value;
428+ }
429+
430+ Type value() const { return m_value; }
431+
432+private:
433+ Type m_value;
434+ Type m_maxDelta;
435+};
436+
437+/*
438+ A point that has its movement dampened.
439+ */
440+class DampedPointF {
441+public:
442+ void setMaxDelta(qreal maxDelta) {
443+ m_x.setMaxDelta(maxDelta);
444+ m_y.setMaxDelta(maxDelta);
445+ }
446+
447+ qreal maxDelta() const { return m_x.maxDelta(); }
448+
449+ void reset(const QPointF &point) {
450+ m_x.reset(point.x());
451+ m_y.reset(point.y());
452+ }
453+
454+ void update(const QPointF &point) {
455+ m_x.update(point.x());
456+ m_y.update(point.y());
457+ }
458+
459+ qreal x() const { return m_x.value(); }
460+ qreal y() const { return m_y.value(); }
461+private:
462+ Damper<qreal> m_x;
463+ Damper<qreal> m_y;
464+};
465+
466+#endif // UBUNTU_GESTURES_DAMPER_H
467
468=== added file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp'
469--- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 1970-01-01 00:00:00 +0000
470+++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2013-05-20 13:14:26 +0000
471@@ -0,0 +1,292 @@
472+/*
473+ * Copyright (C) 2013 Canonical, Ltd.
474+ *
475+ * This program is free software; you can redistribute it and/or modify
476+ * it under the terms of the GNU General Public License as published by
477+ * the Free Software Foundation; version 3.
478+ *
479+ * This program is distributed in the hope that it will be useful,
480+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
481+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
482+ * GNU General Public License for more details.
483+ *
484+ * You should have received a copy of the GNU General Public License
485+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
486+ */
487+
488+#include "DirectionalDragArea.h"
489+
490+#include <QtCore/qmath.h>
491+
492+DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
493+ : QQuickItem(parent)
494+ , m_status(WaitingForTouch)
495+ , m_touchId(-1)
496+ , m_direction(DirectionalDragArea::Rightwards)
497+ , m_wideningAngle(0)
498+ , m_wideningFactor(0)
499+ , m_distanceThreshold(0)
500+{
501+}
502+
503+DirectionalDragArea::Direction DirectionalDragArea::direction() const
504+{
505+ return m_direction;
506+}
507+
508+void DirectionalDragArea::setDirection(DirectionalDragArea::Direction direction)
509+{
510+ if (direction != m_direction) {
511+ m_direction = direction;
512+ Q_EMIT directionChanged(m_direction);
513+ }
514+}
515+
516+void DirectionalDragArea::setMaxDeviation(qreal value)
517+{
518+ if (m_dampedPos.maxDelta() != value) {
519+ m_dampedPos.setMaxDelta(value);
520+ Q_EMIT maxDeviationChanged(value);
521+ }
522+}
523+
524+qreal DirectionalDragArea::wideningAngle() const
525+{
526+ return m_wideningAngle;
527+}
528+
529+void DirectionalDragArea::setWideningAngle(qreal angle)
530+{
531+ if (angle == m_wideningAngle)
532+ return;
533+
534+ m_wideningAngle = angle;
535+ m_wideningFactor = qTan(angle * M_PI / 180.0);
536+ Q_EMIT wideningAngleChanged(angle);
537+}
538+
539+void DirectionalDragArea::setDistanceThreshold(qreal value)
540+{
541+ if (m_distanceThreshold != value) {
542+ m_distanceThreshold = value;
543+ Q_EMIT distanceThresholdChanged(value);
544+ }
545+}
546+
547+qreal DirectionalDragArea::distance() const
548+{
549+ if (directionIsHorizontal()) {
550+ return m_previousPos.x() - m_startPos.x();
551+ } else {
552+ return m_previousPos.y() - m_startPos.y();
553+ }
554+}
555+
556+qreal DirectionalDragArea::touchX() const
557+{
558+ return m_previousPos.x();
559+}
560+
561+qreal DirectionalDragArea::touchY() const
562+{
563+ return m_previousPos.y();
564+}
565+
566+void DirectionalDragArea::touchEvent(QTouchEvent *event)
567+{
568+ if (!isEnabled() || !isVisible()) {
569+ QQuickItem::touchEvent(event);
570+ return;
571+ }
572+
573+ switch (m_status) {
574+ case WaitingForTouch:
575+ touchEvent_absent(event);
576+ break;
577+ case Undecided:
578+ touchEvent_undecided(event);
579+ break;
580+ case Recognized:
581+ touchEvent_recognized(event);
582+ break;
583+ default: // Rejected
584+ touchEvent_rejected(event);
585+ break;
586+ }
587+}
588+
589+void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
590+{
591+ if ((event->touchPointStates() && (Qt::TouchPointPressed || Qt::TouchPointMoved))
592+ && event->touchPoints().count() == 1) {
593+ m_startPos = event->touchPoints()[0].pos();
594+ m_touchId = event->touchPoints()[0].id();
595+ m_dampedPos.reset(m_startPos);
596+ setStatus(Undecided);
597+ setPreviousPos(m_startPos);
598+ }
599+}
600+
601+void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
602+{
603+ const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
604+ const QPointF &touchPos = touchPoint->pos();
605+
606+ if (touchPoint->state() == Qt::TouchPointReleased) {
607+ // touch has ended before recognition concluded
608+ setStatus(WaitingForTouch);
609+ return;
610+ }
611+
612+ if (event->touchPointStates().testFlag(Qt::TouchPointPressed)
613+ || event->touchPoints().count() > 1) {
614+ // multi-finger drags are not accepted
615+ setStatus(Rejected);
616+ return;
617+ }
618+
619+ m_previousDampedPos.setX(m_dampedPos.x());
620+ m_previousDampedPos.setY(m_dampedPos.y());
621+ m_dampedPos.update(touchPos);
622+
623+ if (!pointInsideAllowedArea()) {
624+ setStatus(Rejected);
625+ return;
626+ }
627+
628+ if (!movingInRightDirection()) {
629+ setStatus(Rejected);
630+ return;
631+ }
632+
633+ setPreviousPos(touchPos);
634+
635+ if (movedFarEnough(touchPos)) {
636+ setStatus(Recognized);
637+ }
638+}
639+
640+void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
641+{
642+ const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
643+
644+ setPreviousPos(touchPoint->pos());
645+
646+ if (touchPoint->state() == Qt::TouchPointReleased) {
647+ setStatus(WaitingForTouch);
648+ }
649+}
650+
651+void DirectionalDragArea::touchEvent_rejected(QTouchEvent *event)
652+{
653+ const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
654+
655+ if (!touchPoint || touchPoint->state() == Qt::TouchPointReleased) {
656+ setStatus(WaitingForTouch);
657+ }
658+}
659+
660+const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
661+{
662+ const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
663+ const QTouchEvent::TouchPoint *touchPoint = 0;
664+ for (int i = 0; i < touchPoints.size(); ++i) {
665+ if (touchPoints.at(i).id() == m_touchId) {
666+ touchPoint = &touchPoints.at(i);
667+ break;
668+ }
669+ }
670+ return touchPoint;
671+}
672+
673+bool DirectionalDragArea::pointInsideAllowedArea() const
674+{
675+ qreal dX = m_dampedPos.x() - m_startPos.x();
676+ qreal dY = m_dampedPos.y() - m_startPos.y();
677+
678+ switch (m_direction) {
679+ case Upwards:
680+ return dY <= 0 && qFabs(dX) <= qFabs(dY) * m_wideningFactor;
681+ case Downwards:
682+ return dY >= 0 && qFabs(dX) <= dY * m_wideningFactor;
683+ case Leftwards:
684+ return dX <= 0 && qFabs(dY) <= qFabs(dX) * m_wideningFactor;
685+ default: // Rightwards:
686+ return dX >= 0 && qFabs(dY) <= dX * m_wideningFactor;
687+ }
688+}
689+
690+bool DirectionalDragArea::movingInRightDirection() const
691+{
692+ switch (m_direction) {
693+ case Upwards:
694+ return m_dampedPos.y() <= m_previousDampedPos.y();
695+ case Downwards:
696+ return m_dampedPos.y() >= m_previousDampedPos.y();
697+ case Leftwards:
698+ return m_dampedPos.x() <= m_previousDampedPos.x();
699+ default: // Rightwards:
700+ return m_dampedPos.x() >= m_previousDampedPos.x();
701+ }
702+}
703+
704+bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
705+{
706+ if (directionIsHorizontal())
707+ return qFabs(point.x() - m_startPos.x()) > m_distanceThreshold;
708+ else
709+ return qFabs(point.y() - m_startPos.y()) > m_distanceThreshold;
710+}
711+
712+void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
713+{
714+ if (newStatus == m_status)
715+ return;
716+
717+ m_status = newStatus;
718+ Q_EMIT statusChanged(m_status);
719+
720+ switch (newStatus) {
721+ case WaitingForTouch:
722+ Q_EMIT draggingChanged(false);
723+ break;
724+ case Undecided:
725+ Q_EMIT draggingChanged(true);
726+ break;
727+ default: // Rejected
728+ // no-op
729+ break;
730+ }
731+}
732+
733+void DirectionalDragArea::setPreviousPos(QPointF point)
734+{
735+ Q_ASSERT(m_status != Rejected);
736+
737+ bool xChanged = m_previousPos.x() != point.x();
738+ bool yChanged = m_previousPos.y() != point.y();
739+
740+ m_previousPos = point;
741+
742+ if (xChanged) {
743+ Q_EMIT touchXChanged(point.x());
744+ if (directionIsHorizontal())
745+ Q_EMIT distanceChanged(distance());
746+ }
747+
748+ if (yChanged) {
749+ Q_EMIT touchYChanged(point.y());
750+ if (directionIsVertical())
751+ Q_EMIT distanceChanged(distance());
752+ }
753+}
754+
755+bool DirectionalDragArea::directionIsHorizontal() const
756+{
757+ return m_direction == Leftwards || m_direction == Rightwards;
758+}
759+
760+bool DirectionalDragArea::directionIsVertical() const
761+{
762+ return m_direction == Upwards || m_direction == Downwards;
763+}
764
765=== added file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h'
766--- plugins/Ubuntu/Gestures/DirectionalDragArea.h 1970-01-01 00:00:00 +0000
767+++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2013-05-20 13:14:26 +0000
768@@ -0,0 +1,179 @@
769+/*
770+ * Copyright (C) 2013 Canonical, Ltd.
771+ *
772+ * This program is free software; you can redistribute it and/or modify
773+ * it under the terms of the GNU General Public License as published by
774+ * the Free Software Foundation; version 3.
775+ *
776+ * This program is distributed in the hope that it will be useful,
777+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
778+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
779+ * GNU General Public License for more details.
780+ *
781+ * You should have received a copy of the GNU General Public License
782+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
783+ */
784+
785+#ifndef DIRECTIONAL_DRAG_AREA_H
786+#define DIRECTIONAL_DRAG_AREA_H
787+
788+#include <QtQuick/QQuickItem>
789+#include "UbuntuGesturesGlobal.h"
790+#include "Damper.h"
791+
792+/*
793+ An area that detects axis-aligned single-finger drag gestures
794+
795+ If a drag deviates too much from the components' direction recognition will
796+ fail. It will also fail if the drag or flick is too short. E.g. a noisy or
797+ fidgety click
798+
799+ See doc/DirectionalDragArea.svg
800+ */
801+class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {
802+ Q_OBJECT
803+
804+ // The direction in which the gesture should move in order to be recognized.
805+ Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged)
806+
807+ // The distance travelled by the finger along the axis specified by
808+ // DirectionalDragArea's direction.
809+ Q_PROPERTY(qreal distance READ distance NOTIFY distanceChanged)
810+
811+ // Position of the touch point performing the drag.
812+ Q_PROPERTY(qreal touchX READ touchX NOTIFY touchXChanged)
813+ Q_PROPERTY(qreal touchY READ touchY NOTIFY touchYChanged)
814+
815+ // The current status of the directional drag gesture area.
816+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
817+
818+ // Whether a drag gesture is taking place (regardless of whether it's a correct
819+ // single-finger directional drag or not)
820+ Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
821+
822+ /////
823+ // stuff that will be set in stone at some point
824+
825+ // How far the touch point can move away from its expected position before
826+ // it causes a rejection in the gesture recognition. This is to compensate
827+ // for both noise in the touch input signal and for the natural irregularities
828+ // in the finger movement.
829+ // Proper value is likely device-specific.
830+ Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
831+
832+ // Widening angle, in degrees
833+ // It's roughly the maximum angle a touch point can make relative to the
834+ // axis defined by the compoment's direction for it to be recognized as a
835+ // directional drag.
836+ Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
837+ NOTIFY wideningAngleChanged)
838+
839+ // How far a touch point has to move from its initial position in order for
840+ // it to be recognized as a directional drag.
841+ Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
842+ NOTIFY distanceThresholdChanged)
843+
844+ //
845+ /////
846+
847+ Q_ENUMS(Direction)
848+ Q_ENUMS(Status)
849+public:
850+ DirectionalDragArea(QQuickItem *parent = 0);
851+
852+ enum Direction { Rightwards, // Along the positive direction of the X axis
853+ Leftwards, // Along the negative direction of the X axis
854+ Downwards, // Along the positive direction of the Y axis
855+ Upwards }; // Along the negative direction of the Y axis
856+ Direction direction() const;
857+ void setDirection(Direction);
858+
859+ // Describes the state of the directional drag gesture.
860+ enum Status {
861+ // There's no touch point over this area.
862+ WaitingForTouch,
863+
864+ // A touch point has landed on this area but it's not know yet whether it is
865+ // performing a drag in the correct direction.
866+ Undecided, //Recognizing,
867+
868+ // There's a touch point in this area and it performed a drag in the correct
869+ // direction.
870+ //
871+ // Once recognized, the gesture state will move back to Absent only once
872+ // that touch point ends. The gesture will remain in the Recognized state even if
873+ // the touch point starts moving in other directions or halts.
874+ Recognized,
875+
876+ // A gesture was performed but it wasn't a single-touch drag in the correct
877+ // direction.
878+ // It will remain in this state until there are no more touch points over this
879+ // area, at which point it will move to Absent state.
880+ Rejected
881+ };
882+ Status status() const { return m_status; }
883+
884+ qreal distance() const;
885+
886+ qreal touchX() const;
887+ qreal touchY() const;
888+
889+ bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
890+
891+ qreal maxDeviation() const { return m_dampedPos.maxDelta(); }
892+ void setMaxDeviation(qreal value);
893+
894+ qreal wideningAngle() const;
895+ void setWideningAngle(qreal value);
896+
897+ qreal distanceThreshold() const { return m_distanceThreshold; }
898+ void setDistanceThreshold(qreal value);
899+
900+Q_SIGNALS:
901+ void directionChanged(Direction direction);
902+ void statusChanged(Status value);
903+ void draggingChanged(bool value);
904+ void distanceChanged(qreal value);
905+ void maxDeviationChanged(qreal value);
906+ void wideningAngleChanged(qreal value);
907+ void distanceThresholdChanged(qreal value);
908+ void touchXChanged(qreal value);
909+ void touchYChanged(qreal value);
910+
911+protected:
912+ virtual void touchEvent(QTouchEvent *event);
913+
914+private:
915+ void touchEvent_absent(QTouchEvent *event);
916+ void touchEvent_undecided(QTouchEvent *event);
917+ void touchEvent_recognized(QTouchEvent *event);
918+ void touchEvent_rejected(QTouchEvent *event);
919+ bool pointInsideAllowedArea() const;
920+ bool movingInRightDirection() const;
921+ bool movedFarEnough(const QPointF &point) const;
922+ const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
923+ void setStatus(Status newStatus);
924+ void setPreviousPos(QPointF point);
925+
926+ // convenience functions
927+ bool directionIsHorizontal() const;
928+ bool directionIsVertical() const;
929+
930+ Status m_status;
931+
932+ QPointF m_startPos;
933+ QPointF m_previousPos;
934+ int m_touchId;
935+
936+ // A movement damper is used in some of the gesture recognition calculations
937+ // to get rid of noise or small oscillations in the touch position.
938+ DampedPointF m_dampedPos;
939+ QPointF m_previousDampedPos;
940+
941+ Direction m_direction;
942+ qreal m_wideningAngle; // in degrees
943+ qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle))
944+ qreal m_distanceThreshold;
945+};
946+
947+#endif // DIRECTIONAL_DRAG_AREA_H
948
949=== added file 'plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h'
950--- plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h 1970-01-01 00:00:00 +0000
951+++ plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h 2013-05-20 13:14:26 +0000
952@@ -0,0 +1,23 @@
953+/*
954+ * Copyright (C) 2013 Canonical, Ltd.
955+ *
956+ * This program is free software; you can redistribute it and/or modify
957+ * it under the terms of the GNU General Public License as published by
958+ * the Free Software Foundation; version 3.
959+ *
960+ * This program is distributed in the hope that it will be useful,
961+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
962+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
963+ * GNU General Public License for more details.
964+ *
965+ * You should have received a copy of the GNU General Public License
966+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
967+ */
968+
969+#include <QtCore/QtGlobal>
970+
971+#if defined(UBUNTUGESTURES_LIBRARY)
972+# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT
973+#else
974+# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT
975+#endif
976
977=== added file 'plugins/Ubuntu/Gestures/plugin.cpp'
978--- plugins/Ubuntu/Gestures/plugin.cpp 1970-01-01 00:00:00 +0000
979+++ plugins/Ubuntu/Gestures/plugin.cpp 2013-05-20 13:14:26 +0000
980@@ -0,0 +1,25 @@
981+/*
982+ * Copyright (C) 2013 Canonical, Ltd.
983+ *
984+ * This program is free software; you can redistribute it and/or modify
985+ * it under the terms of the GNU General Public License as published by
986+ * the Free Software Foundation; version 3.
987+ *
988+ * This program is distributed in the hope that it will be useful,
989+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
990+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
991+ * GNU General Public License for more details.
992+ *
993+ * You should have received a copy of the GNU General Public License
994+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
995+ */
996+
997+#include "plugin.h"
998+#include "DirectionalDragArea.h"
999+
1000+#include <qqml.h>
1001+
1002+void UbuntuGestureQmlPlugin::registerTypes(const char *uri)
1003+{
1004+ qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea");
1005+}
1006
1007=== added file 'plugins/Ubuntu/Gestures/plugin.h'
1008--- plugins/Ubuntu/Gestures/plugin.h 1970-01-01 00:00:00 +0000
1009+++ plugins/Ubuntu/Gestures/plugin.h 2013-05-20 13:14:26 +0000
1010@@ -0,0 +1,31 @@
1011+/*
1012+ * Copyright (C) 2013 Canonical, Ltd.
1013+ *
1014+ * This program is free software; you can redistribute it and/or modify
1015+ * it under the terms of the GNU General Public License as published by
1016+ * the Free Software Foundation; version 3.
1017+ *
1018+ * This program is distributed in the hope that it will be useful,
1019+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1020+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1021+ * GNU General Public License for more details.
1022+ *
1023+ * You should have received a copy of the GNU General Public License
1024+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1025+ */
1026+
1027+#ifndef PLUGIN_H
1028+#define PLUGIN_H
1029+
1030+#include <QtQml/QQmlExtensionPlugin>
1031+
1032+class UbuntuGestureQmlPlugin : public QQmlExtensionPlugin
1033+{
1034+ Q_OBJECT
1035+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1036+public:
1037+ void registerTypes(const char *uri);
1038+};
1039+
1040+
1041+#endif
1042
1043=== added file 'plugins/Ubuntu/Gestures/qmldir'
1044--- plugins/Ubuntu/Gestures/qmldir 1970-01-01 00:00:00 +0000
1045+++ plugins/Ubuntu/Gestures/qmldir 2013-05-20 13:14:26 +0000
1046@@ -0,0 +1,2 @@
1047+module Ubuntu.Gestures
1048+plugin UbuntuGestureQml
1049
1050=== modified file 'tests/plugins/CMakeLists.txt'
1051--- tests/plugins/CMakeLists.txt 2013-04-16 14:22:23 +0000
1052+++ tests/plugins/CMakeLists.txt 2013-05-20 13:14:26 +0000
1053@@ -1,1 +1,2 @@
1054 add_subdirectory(Utils)
1055+add_subdirectory(Ubuntu)
1056
1057=== added directory 'tests/plugins/Ubuntu'
1058=== added file 'tests/plugins/Ubuntu/CMakeLists.txt'
1059--- tests/plugins/Ubuntu/CMakeLists.txt 1970-01-01 00:00:00 +0000
1060+++ tests/plugins/Ubuntu/CMakeLists.txt 2013-05-20 13:14:26 +0000
1061@@ -0,0 +1,1 @@
1062+add_subdirectory(Gestures)
1063
1064=== added directory 'tests/plugins/Ubuntu/Gestures'
1065=== added file 'tests/plugins/Ubuntu/Gestures/CMakeLists.txt'
1066--- tests/plugins/Ubuntu/Gestures/CMakeLists.txt 1970-01-01 00:00:00 +0000
1067+++ tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2013-05-20 13:14:26 +0000
1068@@ -0,0 +1,38 @@
1069+########## tst_DirectionalDragArea
1070+
1071+include_directories(
1072+ ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
1073+ ${CMAKE_CURRENT_BINARY_DIR}
1074+ )
1075+
1076+set(testCommand
1077+ LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
1078+ ${CMAKE_CURRENT_BINARY_DIR}/DirectionalDragAreaTestExec
1079+ -o ${CMAKE_BINARY_DIR}/DirectionalDragAreaTest.xml,xunitxml
1080+ -o -,txt)
1081+
1082+add_custom_target(DirectionalDragAreaTest ${testCommand})
1083+add_dependencies(qmluitests DirectionalDragAreaTest)
1084+add_dependencies(DirectionalDragAreaTest DirectionalDragAreaTestExec UbutunGesturesTestQmlFiles)
1085+
1086+add_executable(DirectionalDragAreaTestExec tst_DirectionalDragArea.cpp)
1087+qt5_use_modules(DirectionalDragAreaTestExec Test Core Qml Gui Quick)
1088+target_link_libraries(DirectionalDragAreaTestExec UbuntuGestureQml)
1089+
1090+add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
1091+
1092+file(GLOB qmlFiles *.qml)
1093+add_custom_target(UbuntuGesturesTestQmlFiles ALL
1094+ COMMAND cp ${qmlFiles} ${CMAKE_CURRENT_BINARY_DIR}
1095+ DEPENDS ${qmlFiles}
1096+)
1097+
1098+########## tst_Damper
1099+
1100+set(damperTestCommand
1101+ DamperTestExec -o ${CMAKE_BINARY_DIR}/DamperTest.xml,xunitxml -o -,txt)
1102+
1103+add_test(NAME DamperTest COMMAND ${damperTestCommand})
1104+
1105+add_executable(DamperTestExec tst_Damper.cpp)
1106+qt5_use_modules(DamperTestExec Test Core)
1107
1108=== added file 'tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml'
1109--- tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 1970-01-01 00:00:00 +0000
1110+++ tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2013-05-20 13:14:26 +0000
1111@@ -0,0 +1,87 @@
1112+/*
1113+ * Copyright (C) 2013 Canonical, Ltd.
1114+ *
1115+ * This program is free software; you can redistribute it and/or modify
1116+ * it under the terms of the GNU General Public License as published by
1117+ * the Free Software Foundation; version 3.
1118+ *
1119+ * This program is distributed in the hope that it will be useful,
1120+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1121+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1122+ * GNU General Public License for more details.
1123+ *
1124+ * You should have received a copy of the GNU General Public License
1125+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1126+ */
1127+
1128+import QtQuick 2.0
1129+import Ubuntu.Gestures 0.1
1130+import Ubuntu.Components 0.1
1131+
1132+Item {
1133+
1134+ function reset() { launcher.y = -launcher.height }
1135+
1136+ Rectangle {
1137+ id: launcher
1138+ color: "blue"
1139+ width: parent.width
1140+ height: units.gu(15)
1141+ x: 0
1142+ y: followDragArea()
1143+
1144+ function followDragArea() {
1145+ if (dragArea.status === DirectionalDragArea.Rejected)
1146+ return -height
1147+ else
1148+ return dragArea.distance < height ? -height + dragArea.distance : 0
1149+ }
1150+ }
1151+
1152+ Rectangle {
1153+ id: dragAreaRect
1154+ color: "yellow"
1155+ opacity: 0.0
1156+ anchors.fill: dragArea
1157+ }
1158+
1159+ DirectionalDragArea {
1160+ id: dragArea
1161+ objectName: "vpDragArea"
1162+
1163+ height: units.gu(5)
1164+
1165+ direction: DirectionalDragArea.Downwards
1166+ maxDeviation: units.gu(2)
1167+ wideningAngle: 10
1168+ distanceThreshold: units.gu(4)
1169+
1170+ onStatusChanged: {
1171+ switch (status) {
1172+ case DirectionalDragArea.WaitingForTouch:
1173+ dragAreaRect.opacity = 0.0
1174+ break;
1175+ case DirectionalDragArea.Undecided:
1176+ dragAreaRect.color = "yellow"
1177+ dragAreaRect.opacity = 0.3
1178+ launcher.y = Qt.binding(launcher.followDragArea)
1179+ break;
1180+ case DirectionalDragArea.Recognized:
1181+ dragAreaRect.color = "green"
1182+ dragAreaRect.opacity = 0.5
1183+ break;
1184+ default: //case DirectionalDragArea.Rejected:
1185+ dragAreaRect.color = "red"
1186+ dragAreaRect.opacity = 0.5
1187+ launcher.y = -launcher.height
1188+ break;
1189+ }
1190+ }
1191+
1192+ anchors {
1193+ left: parent.left
1194+ right: parent.right
1195+ top: parent.top
1196+ }
1197+ }
1198+}
1199
1200=== added file 'tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml'
1201--- tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 1970-01-01 00:00:00 +0000
1202+++ tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2013-05-20 13:14:26 +0000
1203@@ -0,0 +1,90 @@
1204+/*
1205+ * Copyright (C) 2013 Canonical, Ltd.
1206+ *
1207+ * This program is free software; you can redistribute it and/or modify
1208+ * it under the terms of the GNU General Public License as published by
1209+ * the Free Software Foundation; version 3.
1210+ *
1211+ * This program is distributed in the hope that it will be useful,
1212+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1213+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1214+ * GNU General Public License for more details.
1215+ *
1216+ * You should have received a copy of the GNU General Public License
1217+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1218+ */
1219+
1220+import QtQuick 2.0
1221+import Ubuntu.Gestures 0.1
1222+import Ubuntu.Components 0.1
1223+
1224+Item {
1225+ id: root
1226+
1227+ function reset() { launcher.x = root.width }
1228+
1229+ Rectangle {
1230+ id: launcher
1231+ color: "blue"
1232+ width: units.gu(15)
1233+ height: parent.height
1234+ x: root.width
1235+ y: 0
1236+
1237+ function followDragArea() {
1238+ if (dragArea.status === DirectionalDragArea.Rejected)
1239+ return root.width
1240+ else
1241+ return dragArea.distance > -width ?
1242+ root.width + dragArea.distance
1243+ :
1244+ root.width - width
1245+ }
1246+ }
1247+
1248+ Rectangle {
1249+ id: dragAreaRect
1250+ opacity: 0.0
1251+ anchors.fill: dragArea
1252+ }
1253+
1254+ DirectionalDragArea {
1255+ id: dragArea
1256+ objectName: "hnDragArea"
1257+
1258+ width: units.gu(5)
1259+
1260+ direction: DirectionalDragArea.Leftwards
1261+ maxDeviation: units.gu(2)
1262+ wideningAngle: 10
1263+ distanceThreshold: units.gu(4)
1264+
1265+ onStatusChanged: {
1266+ switch (status) {
1267+ case DirectionalDragArea.WaitingForTouch:
1268+ dragAreaRect.opacity = 0.0
1269+ break;
1270+ case DirectionalDragArea.Undecided:
1271+ dragAreaRect.color = "yellow"
1272+ dragAreaRect.opacity = 0.3
1273+ launcher.x = Qt.binding(launcher.followDragArea)
1274+ break;
1275+ case DirectionalDragArea.Recognized:
1276+ dragAreaRect.color = "green"
1277+ dragAreaRect.opacity = 0.5
1278+ break;
1279+ default: //case DirectionalDragArea.Rejected:
1280+ dragAreaRect.color = "red"
1281+ dragAreaRect.opacity = 0.5
1282+ launcher.x = -launcher.height
1283+ break;
1284+ }
1285+ }
1286+
1287+ anchors {
1288+ right: parent.right
1289+ top: parent.top
1290+ bottom: parent.bottom
1291+ }
1292+ }
1293+}
1294
1295=== added file 'tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml'
1296--- tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 1970-01-01 00:00:00 +0000
1297+++ tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2013-05-20 13:14:26 +0000
1298@@ -0,0 +1,86 @@
1299+/*
1300+ * Copyright (C) 2013 Canonical, Ltd.
1301+ *
1302+ * This program is free software; you can redistribute it and/or modify
1303+ * it under the terms of the GNU General Public License as published by
1304+ * the Free Software Foundation; version 3.
1305+ *
1306+ * This program is distributed in the hope that it will be useful,
1307+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1308+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1309+ * GNU General Public License for more details.
1310+ *
1311+ * You should have received a copy of the GNU General Public License
1312+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1313+ */
1314+
1315+import QtQuick 2.0
1316+import Ubuntu.Gestures 0.1
1317+import Ubuntu.Components 0.1
1318+
1319+Item {
1320+
1321+ function reset() { launcher.x = -launcher.width }
1322+
1323+ Rectangle {
1324+ id: launcher
1325+ color: "blue"
1326+ width: units.gu(15)
1327+ height: parent.height
1328+ x: followDragArea()
1329+ y: 0
1330+
1331+ function followDragArea() {
1332+ if (dragArea.status === DirectionalDragArea.Rejected)
1333+ return -width
1334+ else
1335+ return dragArea.distance < width ? -width + dragArea.distance : 0
1336+ }
1337+ }
1338+
1339+ Rectangle {
1340+ id: dragAreaRect
1341+ opacity: 0.0
1342+ anchors.fill: dragArea
1343+ }
1344+
1345+ DirectionalDragArea {
1346+ id: dragArea
1347+ objectName: "hpDragArea"
1348+
1349+ width: units.gu(5)
1350+
1351+ direction: DirectionalDragArea.Rightwards
1352+ maxDeviation: units.gu(2)
1353+ wideningAngle: 10
1354+ distanceThreshold: units.gu(4)
1355+
1356+ onStatusChanged: {
1357+ switch (status) {
1358+ case DirectionalDragArea.WaitingForTouch:
1359+ dragAreaRect.opacity = 0.0
1360+ break;
1361+ case DirectionalDragArea.Undecided:
1362+ dragAreaRect.color = "yellow"
1363+ dragAreaRect.opacity = 0.3
1364+ launcher.x = Qt.binding(launcher.followDragArea)
1365+ break;
1366+ case DirectionalDragArea.Recognized:
1367+ dragAreaRect.color = "green"
1368+ dragAreaRect.opacity = 0.5
1369+ break;
1370+ default: //case DirectionalDragArea.Rejected:
1371+ dragAreaRect.color = "red"
1372+ dragAreaRect.opacity = 0.5
1373+ launcher.x = -launcher.height
1374+ break;
1375+ }
1376+ }
1377+
1378+ anchors {
1379+ left: parent.left
1380+ top: parent.top
1381+ bottom: parent.bottom
1382+ }
1383+ }
1384+}
1385
1386=== added file 'tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml'
1387--- tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 1970-01-01 00:00:00 +0000
1388+++ tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2013-05-20 13:14:26 +0000
1389@@ -0,0 +1,90 @@
1390+/*
1391+ * Copyright (C) 2013 Canonical, Ltd.
1392+ *
1393+ * This program is free software; you can redistribute it and/or modify
1394+ * it under the terms of the GNU General Public License as published by
1395+ * the Free Software Foundation; version 3.
1396+ *
1397+ * This program is distributed in the hope that it will be useful,
1398+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1399+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1400+ * GNU General Public License for more details.
1401+ *
1402+ * You should have received a copy of the GNU General Public License
1403+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1404+ */
1405+
1406+import QtQuick 2.0
1407+import Ubuntu.Gestures 0.1
1408+import Ubuntu.Components 0.1
1409+
1410+Item {
1411+ id: root
1412+
1413+ function reset() { launcher.y = root.height }
1414+
1415+ Rectangle {
1416+ id: launcher
1417+ color: "blue"
1418+ width: parent.width
1419+ height: units.gu(15)
1420+ x: 0
1421+ y: root.height
1422+
1423+ function followDragArea() {
1424+ if (dragArea.status === DirectionalDragArea.Rejected)
1425+ return root.height
1426+ else
1427+ return dragArea.distance > -height ?
1428+ root.height + dragArea.distance
1429+ :
1430+ root.height - height
1431+ }
1432+ }
1433+
1434+ Rectangle {
1435+ id: dragAreaRect
1436+ opacity: 0.0
1437+ anchors.fill: dragArea
1438+ }
1439+
1440+ DirectionalDragArea {
1441+ id: dragArea
1442+ objectName: "vnDragArea"
1443+
1444+ height: units.gu(5)
1445+
1446+ direction: DirectionalDragArea.Upwards
1447+ maxDeviation: units.gu(2)
1448+ wideningAngle: 10
1449+ distanceThreshold: units.gu(4)
1450+
1451+ onStatusChanged: {
1452+ switch (status) {
1453+ case DirectionalDragArea.WaitingForTouch:
1454+ dragAreaRect.opacity = 0.0
1455+ break;
1456+ case DirectionalDragArea.Undecided:
1457+ dragAreaRect.color = "yellow"
1458+ dragAreaRect.opacity = 0.3
1459+ launcher.y = Qt.binding(launcher.followDragArea)
1460+ break;
1461+ case DirectionalDragArea.Recognized:
1462+ dragAreaRect.color = "green"
1463+ dragAreaRect.opacity = 0.5
1464+ break;
1465+ default: //case DirectionalDragArea.Rejected:
1466+ dragAreaRect.color = "red"
1467+ dragAreaRect.opacity = 0.5
1468+ launcher.y = -launcher.height
1469+ break;
1470+ }
1471+ }
1472+
1473+ anchors {
1474+ left: parent.left
1475+ right: parent.right
1476+ bottom: parent.bottom
1477+ }
1478+ }
1479+}
1480
1481=== added file 'tests/plugins/Ubuntu/Gestures/edgeDragExample.qml'
1482--- tests/plugins/Ubuntu/Gestures/edgeDragExample.qml 1970-01-01 00:00:00 +0000
1483+++ tests/plugins/Ubuntu/Gestures/edgeDragExample.qml 2013-05-20 13:14:26 +0000
1484@@ -0,0 +1,40 @@
1485+/*
1486+ * Copyright (C) 2013 Canonical, Ltd.
1487+ *
1488+ * This program is free software; you can redistribute it and/or modify
1489+ * it under the terms of the GNU General Public License as published by
1490+ * the Free Software Foundation; version 3.
1491+ *
1492+ * This program is distributed in the hope that it will be useful,
1493+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1494+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1495+ * GNU General Public License for more details.
1496+ *
1497+ * You should have received a copy of the GNU General Public License
1498+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1499+ */
1500+
1501+import QtQuick 2.0
1502+import Ubuntu.Components 0.1
1503+
1504+Rectangle {
1505+ //width: units.gu(40)
1506+ //height: units.gu(71)
1507+ color: "white"
1508+
1509+ MouseArea {
1510+ id: mouseArea
1511+ anchors.fill: parent
1512+ onClicked: {
1513+ hpLauncher.reset()
1514+ hnLauncher.reset()
1515+ vpLauncher.reset()
1516+ vnLauncher.reset()
1517+ }
1518+ }
1519+
1520+ RightwardsLauncher { id: hpLauncher; anchors.fill: parent }
1521+ LeftwardsLauncher { id: hnLauncher; anchors.fill: parent }
1522+ DownwardsLauncher { id: vpLauncher; anchors.fill: parent }
1523+ UpwardsLauncher { id: vnLauncher; anchors.fill: parent }
1524+}
1525
1526=== added file 'tests/plugins/Ubuntu/Gestures/tst_Damper.cpp'
1527--- tests/plugins/Ubuntu/Gestures/tst_Damper.cpp 1970-01-01 00:00:00 +0000
1528+++ tests/plugins/Ubuntu/Gestures/tst_Damper.cpp 2013-05-20 13:14:26 +0000
1529@@ -0,0 +1,40 @@
1530+/*
1531+ * Copyright (C) 2013 Canonical, Ltd.
1532+ *
1533+ * This program is free software; you can redistribute it and/or modify
1534+ * it under the terms of the GNU General Public License as published by
1535+ * the Free Software Foundation; version 3.
1536+ *
1537+ * This program is distributed in the hope that it will be useful,
1538+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1539+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1540+ * GNU General Public License for more details.
1541+ *
1542+ * You should have received a copy of the GNU General Public License
1543+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1544+ */
1545+
1546+#include <QtTest/QtTest>
1547+
1548+#include <Damper.h>
1549+
1550+class tst_Damper : public QObject
1551+{
1552+ Q_OBJECT
1553+private Q_SLOTS:
1554+ void negativeMovement();
1555+};
1556+
1557+void tst_Damper::negativeMovement()
1558+{
1559+ Damper<qreal> damper;
1560+
1561+ damper.setMaxDelta(3.0);
1562+ damper.reset(0.0);
1563+ damper.update(-5.0);
1564+ QCOMPARE(damper.value(), -2.0);
1565+}
1566+
1567+QTEST_MAIN(tst_Damper)
1568+
1569+#include "tst_Damper.moc"
1570
1571=== added file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp'
1572--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 1970-01-01 00:00:00 +0000
1573+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2013-05-20 13:14:26 +0000
1574@@ -0,0 +1,275 @@
1575+/*
1576+ * Copyright (C) 2013 Canonical, Ltd.
1577+ *
1578+ * This program is free software; you can redistribute it and/or modify
1579+ * it under the terms of the GNU General Public License as published by
1580+ * the Free Software Foundation; version 3.
1581+ *
1582+ * This program is distributed in the hope that it will be useful,
1583+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1584+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1585+ * GNU General Public License for more details.
1586+ *
1587+ * You should have received a copy of the GNU General Public License
1588+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1589+ */
1590+
1591+#include <QtTest/QtTest>
1592+#include <QtCore/QObject>
1593+#include <qpa/qwindowsysteminterface.h>
1594+#include <QtQuick/QQuickView>
1595+#include <QtQml/QQmlEngine>
1596+
1597+#include <DirectionalDragArea.h>
1598+
1599+class tst_DirectionalDragArea: public QObject
1600+{
1601+ Q_OBJECT
1602+public:
1603+ tst_DirectionalDragArea() : device(0) { }
1604+private Q_SLOTS:
1605+ void initTestCase(); // will be called before the first test function is executed
1606+ void cleanupTestCase(); // will be called after the last test function was executed.
1607+
1608+ void edgeDrag();
1609+ void edgeDrag_data();
1610+ void dragWithShortDirectionChange();
1611+
1612+private:
1613+ QQuickView *createView();
1614+ QTouchDevice *device;
1615+};
1616+
1617+void tst_DirectionalDragArea::initTestCase()
1618+{
1619+ if (!device) {
1620+ device = new QTouchDevice;
1621+ device->setType(QTouchDevice::TouchScreen);
1622+ QWindowSystemInterface::registerTouchDevice(device);
1623+ }
1624+}
1625+
1626+void tst_DirectionalDragArea::cleanupTestCase()
1627+{
1628+}
1629+
1630+QQuickView *tst_DirectionalDragArea::createView()
1631+{
1632+ QQuickView *window = new QQuickView(0);
1633+ window->setResizeMode(QQuickView::SizeRootObjectToView);
1634+ window->resize(600, 600);
1635+ window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
1636+
1637+ return window;
1638+}
1639+
1640+namespace {
1641+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
1642+{
1643+ switch (edgeDragArea->direction()) {
1644+ case DirectionalDragArea::Upwards:
1645+ return QPointF(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
1646+ case DirectionalDragArea::Downwards:
1647+ return QPointF(view->width()/2.0f, edgeDragArea->height()/2.0f);
1648+ case DirectionalDragArea::Leftwards:
1649+ return QPointF(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
1650+ default: // DirectionalDragArea::Rightwards:
1651+ return QPointF(edgeDragArea->width()/2.0f, view->height()/2.0f);
1652+ }
1653+}
1654+
1655+QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
1656+ qreal wideningAngleMultiplier)
1657+{
1658+ qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
1659+ * M_PI / 180.0;
1660+
1661+ qreal angleCos = qCos(angleRadians);
1662+ qreal angleSin = qSin(angleRadians);
1663+
1664+ switch (edgeDragArea->direction()) {
1665+ case DirectionalDragArea::Upwards:
1666+ return QPointF(angleSin, -angleCos);
1667+ case DirectionalDragArea::Downwards:
1668+ return QPointF(angleSin, angleCos);
1669+ case DirectionalDragArea::Leftwards:
1670+ return QPointF(-angleCos, angleSin);
1671+ default: // DirectionalDragArea::Rightwards:
1672+ return QPointF(angleCos, angleSin);
1673+ }
1674+}
1675+
1676+QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
1677+{
1678+ qreal deviation = edgeDragArea->maxDeviation() * 0.8;
1679+
1680+ if (edgeDragArea->direction() == DirectionalDragArea::Leftwards
1681+ || edgeDragArea->direction() == DirectionalDragArea::Rightwards) {
1682+ return QPointF(0, deviation);
1683+ } else {
1684+ return QPointF(deviation, 0);
1685+ }
1686+}
1687+}
1688+
1689+void tst_DirectionalDragArea::edgeDrag()
1690+{
1691+ QFETCH(QString, dragAreaObjectName);
1692+ QFETCH(qreal, wideningAngleMultiplier);
1693+ QFETCH(qreal, dragDistanceFactor);
1694+ QFETCH(int, expectedGestureRecognition);
1695+
1696+ QQuickView *view = createView();
1697+ QScopedPointer<QQuickView> scope(view);
1698+ view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
1699+ view->show();
1700+ QVERIFY(QTest::qWaitForWindowExposed(view));
1701+ QVERIFY(view->rootObject() != 0);
1702+ qApp->processEvents();
1703+
1704+ DirectionalDragArea *edgeDragArea =
1705+ view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
1706+ QVERIFY(edgeDragArea != 0);
1707+
1708+ QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
1709+
1710+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
1711+ QPointF touchPoint = initialTouchPos;
1712+
1713+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
1714+ QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
1715+ wideningAngleMultiplier);
1716+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
1717+
1718+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
1719+
1720+ QCOMPARE(draggingSpy.count(), 1);
1721+ QCOMPARE(edgeDragArea->dragging(), true);
1722+
1723+ if (wideningAngleMultiplier > 0) {
1724+ // go close to the border of the valid area for this touch point
1725+ // in order to make it easier to leave it by dragging at an angle
1726+ // slightly bigger than the widening angle
1727+ touchPoint += createTouchDeviation(edgeDragArea);
1728+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
1729+ }
1730+
1731+ do {
1732+ touchPoint += touchMovement;
1733+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
1734+ } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
1735+
1736+ if (expectedGestureRecognition)
1737+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1738+
1739+ if (edgeDragArea->status() == DirectionalDragArea::Rejected)
1740+ QCOMPARE(edgeDragArea->dragging(), false);
1741+
1742+ QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
1743+
1744+ QCOMPARE(draggingSpy.count(), 2);
1745+ QCOMPARE(edgeDragArea->dragging(), false);
1746+}
1747+
1748+void tst_DirectionalDragArea::edgeDrag_data()
1749+{
1750+ QTest::addColumn<QString>("dragAreaObjectName");
1751+ QTest::addColumn<qreal>("wideningAngleMultiplier");
1752+ QTest::addColumn<qreal>("dragDistanceFactor");
1753+ QTest::addColumn<int>("expectedGestureRecognition");
1754+
1755+ QTest::newRow("rightwards, tiny drag")
1756+ << "hpDragArea" << 0.0 << 0.2 << 0;
1757+
1758+ QTest::newRow("rightwards, straight drag")
1759+ << "hpDragArea" << 0.0 << 3.0 << 1;
1760+
1761+ QTest::newRow("rightwards, diagonal drag")
1762+ << "hpDragArea" << 0.9 << 3.0 << 1;
1763+
1764+ QTest::newRow("rightwards, overly diagonal drag")
1765+ << "hpDragArea" << 2.0 << 3.0 << 0;
1766+
1767+ QTest::newRow("leftwards, tiny drag")
1768+ << "hnDragArea" << 0.0 << 0.2 << 0;
1769+
1770+ QTest::newRow("leftwards, straight drag")
1771+ << "hnDragArea" << 0.0 << 3.0 << 1;
1772+
1773+ QTest::newRow("leftwards, diagonal drag")
1774+ << "hnDragArea" << 0.9 << 3.0 << 1;
1775+
1776+ QTest::newRow("downwards, tiny drag")
1777+ << "vpDragArea" << 0.0 << 0.2 << 0;
1778+
1779+ QTest::newRow("downwards, straight drag")
1780+ << "vpDragArea" << 0.0 << 3.0 << 1;
1781+
1782+ QTest::newRow("downwards, diagonal drag")
1783+ << "vpDragArea" << 0.9 << 3.0 << 1;
1784+
1785+ QTest::newRow("upwards, tiny drag")
1786+ << "vnDragArea" << 0.0 << 0.2 << 0;
1787+
1788+ QTest::newRow("upwards, straight drag")
1789+ << "vnDragArea" << 0.0 << 3.0 << 1;
1790+
1791+ QTest::newRow("upwards, diagonal drag")
1792+ << "vnDragArea" << 0.9 << 3.0 << 1;
1793+
1794+ QTest::newRow("upwards, overly diagonal drag")
1795+ << "vnDragArea" << 2.0 << 3.0 << 0;
1796+}
1797+
1798+/*
1799+ A directional drag should still be recognized if there is a momentaneous, small,
1800+ change in the direction of a drag. That should be accounted as input noise and
1801+ therefore ignored.
1802+ */
1803+void tst_DirectionalDragArea::dragWithShortDirectionChange()
1804+{
1805+ QQuickView *view = createView();
1806+ QScopedPointer<QQuickView> scope(view);
1807+ view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
1808+ view->show();
1809+ QVERIFY(QTest::qWaitForWindowExposed(view));
1810+ QVERIFY(view->rootObject() != 0);
1811+ qApp->processEvents();
1812+
1813+ DirectionalDragArea *edgeDragArea =
1814+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1815+ QVERIFY(edgeDragArea != 0);
1816+
1817+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
1818+ QPointF touchPoint = initialTouchPos;
1819+
1820+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
1821+ QPointF dragDirectionVector(1.0, 0.0);
1822+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
1823+
1824+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
1825+
1826+ // Move a bit in the proper direction
1827+ for (int i=0; i < 3; ++i) {
1828+ touchPoint += touchMovement;
1829+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
1830+ }
1831+
1832+ // Then a sudden and small movement to the opposite direction
1833+ touchPoint -= dragDirectionVector * (edgeDragArea->maxDeviation() * 0.7);
1834+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
1835+
1836+ // And then resume movment in the correct direction until it crosses the distance threshold.
1837+ do {
1838+ touchPoint += touchMovement;
1839+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
1840+ } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
1841+
1842+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1843+
1844+ QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
1845+}
1846+
1847+QTEST_MAIN(tst_DirectionalDragArea)
1848+
1849+#include "tst_DirectionalDragArea.moc"

Subscribers

People subscribed via source and target branches