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
=== modified file 'debian/qml-phone-shell.install'
--- debian/qml-phone-shell.install 2013-05-16 12:02:23 +0000
+++ debian/qml-phone-shell.install 2013-05-20 13:14:26 +0000
@@ -16,3 +16,4 @@
16/usr/share/qml-phone-shell/plugins/LightDM/*16/usr/share/qml-phone-shell/plugins/LightDM/*
17/usr/share/qml-phone-shell/plugins/Unity/*17/usr/share/qml-phone-shell/plugins/Unity/*
18/usr/share/qml-phone-shell/plugins/Utils/*18/usr/share/qml-phone-shell/plugins/Utils/*
19/usr/share/qml-phone-shell/plugins/Ubuntu/*
1920
=== added file 'doc/DirectionalDragArea.svg'
--- doc/DirectionalDragArea.svg 1970-01-01 00:00:00 +0000
+++ doc/DirectionalDragArea.svg 2013-05-20 13:14:26 +0000
@@ -0,0 +1,311 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="644.35803"
13 height="626.32886"
14 id="svg2"
15 version="1.1"
16 inkscape:version="0.48.4 r9939"
17 sodipodi:docname="DirectionalDragArea.svg">
18 <defs
19 id="defs4">
20 <linearGradient
21 id="linearGradient6853">
22 <stop
23 style="stop-color:#00adff;stop-opacity:1;"
24 offset="0"
25 id="stop6855" />
26 <stop
27 id="stop6861"
28 offset="0.82191783"
29 style="stop-color:#00adff;stop-opacity:0.49803922;" />
30 <stop
31 style="stop-color:#000000;stop-opacity:0;"
32 offset="1"
33 id="stop6857" />
34 </linearGradient>
35 <marker
36 inkscape:stockid="Arrow1Send"
37 orient="auto"
38 refY="0"
39 refX="0"
40 id="Arrow1Send"
41 style="overflow:visible">
42 <path
43 id="path4958"
44 d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
45 style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
46 transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
47 inkscape:connector-curvature="0" />
48 </marker>
49 <marker
50 inkscape:stockid="Arrow1Sstart"
51 orient="auto"
52 refY="0"
53 refX="0"
54 id="Arrow1Sstart"
55 style="overflow:visible">
56 <path
57 id="path4955"
58 d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
59 style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
60 transform="matrix(0.2,0,0,0.2,1.2,0)"
61 inkscape:connector-curvature="0" />
62 </marker>
63 <marker
64 inkscape:stockid="Arrow1Mstart"
65 orient="auto"
66 refY="0"
67 refX="0"
68 id="Arrow1Mstart"
69 style="overflow:visible">
70 <path
71 id="path4949"
72 d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
73 style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
74 transform="matrix(0.4,0,0,0.4,4,0)"
75 inkscape:connector-curvature="0" />
76 </marker>
77 <marker
78 inkscape:stockid="Arrow1Sstart"
79 orient="auto"
80 refY="0"
81 refX="0"
82 id="Arrow1Sstart-4"
83 style="overflow:visible">
84 <path
85 inkscape:connector-curvature="0"
86 id="path4955-5"
87 d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
88 style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
89 transform="matrix(0.2,0,0,0.2,1.2,0)" />
90 </marker>
91 <marker
92 inkscape:stockid="Arrow1Send"
93 orient="auto"
94 refY="0"
95 refX="0"
96 id="Arrow1Send-4"
97 style="overflow:visible">
98 <path
99 inkscape:connector-curvature="0"
100 id="path4958-6"
101 d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
102 style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
103 transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
104 </marker>
105 </defs>
106 <sodipodi:namedview
107 id="base"
108 pagecolor="#ffffff"
109 bordercolor="#666666"
110 borderopacity="1.0"
111 inkscape:pageopacity="1"
112 inkscape:pageshadow="2"
113 inkscape:zoom="1.4158879"
114 inkscape:cx="311.5"
115 inkscape:cy="313"
116 inkscape:document-units="px"
117 inkscape:current-layer="layer1"
118 showgrid="false"
119 showguides="true"
120 inkscape:guide-bbox="true"
121 fit-margin-top="5"
122 fit-margin-left="5"
123 fit-margin-right="5"
124 fit-margin-bottom="5"
125 inkscape:window-width="1920"
126 inkscape:window-height="1056"
127 inkscape:window-x="0"
128 inkscape:window-y="24"
129 inkscape:window-maximized="1" />
130 <metadata
131 id="metadata7">
132 <rdf:RDF>
133 <cc:Work
134 rdf:about="">
135 <dc:format>image/svg+xml</dc:format>
136 <dc:type
137 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
138 <dc:title />
139 </cc:Work>
140 </rdf:RDF>
141 </metadata>
142 <g
143 inkscape:label="Layer 1"
144 inkscape:groupmode="layer"
145 id="layer1"
146 transform="translate(-253.65772,218.97803)">
147 <path
148 style="fill:#00adff;fill-opacity:0.75686275;stroke:none"
149 d="M 259.64804,57.193502 792.57928,-142.14719 793.2573,341.96589 259.60777,174.1189 z"
150 id="path6851"
151 inkscape:connector-curvature="0"
152 sodipodi:nodetypes="ccccc" />
153 <path
154 style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
155 d="m 890.84952,-178.96696 -631.14314,236.973167 0,116.242063 630.29325,197.35579"
156 id="path4887"
157 inkscape:connector-curvature="0"
158 sodipodi:nodetypes="cccc" />
159 <path
160 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"
161 d="m 260.83781,59.275793 630.97079,0"
162 id="path4891"
163 inkscape:connector-curvature="0" />
164 <path
165 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"
166 d="m 260.99944,172.08634 630.97079,0"
167 id="path4891-7"
168 inkscape:connector-curvature="0" />
169 <path
170 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"
171 d="m 793.23579,-189.85001 0,591.15535"
172 id="path4911"
173 inkscape:connector-curvature="0"
174 sodipodi:nodetypes="cc" />
175 <path
176 sodipodi:type="arc"
177 style="fill:none;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
178 id="path4915"
179 sodipodi:cx="259.74588"
180 sodipodi:cy="211.80014"
181 sodipodi:rx="18.943148"
182 sodipodi:ry="18.943148"
183 d="m 277.40703,204.94983 a 18.943148,18.943148 0 0 1 1.28197,6.81725"
184 transform="matrix(2.987119,0,0,2.987119,-515.9493,-573.75675)"
185 sodipodi:open="true"
186 sodipodi:start="5.9131755"
187 sodipodi:end="6.28144" />
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-5"
192 sodipodi:cx="259.74588"
193 sodipodi:cy="211.80014"
194 sodipodi:rx="18.943148"
195 sodipodi:ry="18.943148"
196 d="m 278.67886,211.17971 a 18.943148,18.943148 0 0 1 -0.8432,6.24205"
197 transform="matrix(2.987119,0,0,2.987119,-517.22894,-458.90756)"
198 sodipodi:start="6.250427"
199 sodipodi:end="6.5844864"
200 sodipodi:open="true" />
201 <path
202 sodipodi:type="arc"
203 style="fill:#000000;fill-opacity:1;stroke:none"
204 id="path4935"
205 sodipodi:cx="282.05548"
206 sodipodi:cy="227.78796"
207 sodipodi:rx="1.00761"
208 sodipodi:ry="1.00761"
209 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"
210 transform="matrix(2.987119,0,0,2.987119,-525.77343,-564.0127)" />
211 <path
212 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)"
213 d="m 317.09757,65.254063 0,42.548767"
214 id="path4937"
215 inkscape:connector-curvature="0" />
216 <path
217 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)"
218 d="m 308.28828,116.39919 -42.54876,0"
219 id="path4937-9"
220 inkscape:connector-curvature="0" />
221 <text
222 xml:space="preserve"
223 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"
224 x="321.45822"
225 y="92.085648"
226 id="text5787"
227 sodipodi:linespacing="125%"><tspan
228 sodipodi:role="line"
229 id="tspan5789"
230 x="321.45822"
231 y="92.085648"
232 style="font-size:11.94847584px">maxDeviation</tspan></text>
233 <text
234 xml:space="preserve"
235 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"
236 x="322.99289"
237 y="48.58828"
238 id="text5787-2"
239 sodipodi:linespacing="125%"><tspan
240 sodipodi:role="line"
241 id="tspan5789-5"
242 x="322.99289"
243 y="48.58828"
244 style="font-size:11.94847584px">wideningAngle</tspan></text>
245 <path
246 inkscape:connector-curvature="0"
247 id="path5830"
248 d="m 315.70321,-189.85001 0,225.307165"
249 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"
250 sodipodi:nodetypes="cc" />
251 <path
252 style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
253 d="m 315.79005,-204.37393 476.77774,0"
254 id="path5832"
255 inkscape:connector-curvature="0"
256 sodipodi:nodetypes="cc" />
257 <path
258 style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
259 d="m 315.303,-212.93254 0,17.24097"
260 id="path6839"
261 inkscape:connector-curvature="0" />
262 <path
263 inkscape:connector-curvature="0"
264 id="path6841"
265 d="m 792.91626,-212.93254 0,17.24097"
266 style="fill:none;stroke:#000000;stroke-width:2.09098315;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
267 <text
268 sodipodi:linespacing="125%"
269 id="text6843"
270 y="-188.72206"
271 x="483.68591"
272 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"
273 xml:space="preserve"><tspan
274 style="font-size:11.94847584px"
275 y="-188.72206"
276 x="483.68591"
277 id="tspan6845"
278 sodipodi:role="line">distanceThreshold</tspan></text>
279 <text
280 sodipodi:linespacing="125%"
281 id="text6847"
282 y="121.95684"
283 x="321.45822"
284 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"
285 xml:space="preserve"><tspan
286 style="font-size:11.94847584px"
287 y="121.95684"
288 x="321.45822"
289 id="tspan6849"
290 sodipodi:role="line">Position of touch press</tspan></text>
291 <rect
292 style="fill:#00adff;fill-opacity:0.75686275;stroke:none"
293 id="rect6863"
294 width="35.257538"
295 height="35.935566"
296 x="295.58365"
297 y="318.91293" />
298 <text
299 xml:space="preserve"
300 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"
301 x="335.01886"
302 y="340.96045"
303 id="text6865"
304 sodipodi:linespacing="125%"><tspan
305 sodipodi:role="line"
306 id="tspan6867"
307 x="335.01886"
308 y="340.96045"
309 style="font-size:11.94847584px">Valid area for touch position during recognition</tspan></text>
310 </g>
311</svg>
0312
=== modified file 'plugins/CMakeLists.txt'
--- plugins/CMakeLists.txt 2013-04-19 15:06:41 +0000
+++ plugins/CMakeLists.txt 2013-05-20 13:14:26 +0000
@@ -1,3 +1,4 @@
1add_subdirectory(Ubuntu)
1add_subdirectory(Utils)2add_subdirectory(Utils)
2add_subdirectory(Unity)3add_subdirectory(Unity)
3add_subdirectory(HudClient)4add_subdirectory(HudClient)
45
=== added directory 'plugins/Ubuntu'
=== added file 'plugins/Ubuntu/CMakeLists.txt'
--- plugins/Ubuntu/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/CMakeLists.txt 2013-05-20 13:14:26 +0000
@@ -0,0 +1,1 @@
1add_subdirectory(Gestures)
02
=== added directory 'plugins/Ubuntu/Gestures'
=== added file 'plugins/Ubuntu/Gestures/CMakeLists.txt'
--- plugins/Ubuntu/Gestures/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/CMakeLists.txt 2013-05-20 13:14:26 +0000
@@ -0,0 +1,30 @@
1set(CMAKE_AUTOMOC ON)
2
3include(FindPkgConfig)
4find_package(Qt5Core REQUIRED)
5find_package(Qt5Quick REQUIRED)
6
7set(UbuntuGestureQml_SOURCES
8 plugin.cpp
9 DirectionalDragArea.cpp
10)
11
12add_definitions(-DUBUNTUGESTURES_LIBRARY)
13
14add_library(UbuntuGestureQml MODULE ${UbuntuGestureQml_SOURCES})
15
16qt5_use_modules(UbuntuGestureQml Core Quick)
17
18# copy files into build directory for shadow builds
19add_custom_target(UbuntuGestureQmlDirFile ALL
20 COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
21 DEPENDS qmldir
22)
23
24install(TARGETS UbuntuGestureQml
25 DESTINATION ${SHELL_APP_DIR}/plugins/Ubuntu/Gestures
26 )
27
28install(FILES qmldir
29 DESTINATION ${SHELL_APP_DIR}/plugins/Ubuntu/Gestures
30 )
031
=== added file 'plugins/Ubuntu/Gestures/Damper.h'
--- plugins/Ubuntu/Gestures/Damper.h 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/Damper.h 2013-05-20 13:14:26 +0000
@@ -0,0 +1,85 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef UBUNTU_GESTURES_DAMPER_H
18#define UBUNTU_GESTURES_DAMPER_H
19
20#include <QtCore/QPointF>
21
22/*
23 Decreases the oscillations of a value along an axis.
24 */
25template <class Type> class Damper {
26public:
27 // Maximum delta between the raw value and its dampened counterpart.
28 void setMaxDelta(Type maxDelta) {
29 if (maxDelta < 0) qFatal("Damper::maxDelta must be a positive number.");
30 m_maxDelta = maxDelta;
31 }
32 Type maxDelta() const { return m_maxDelta; }
33
34 void reset(Type value) {
35 m_value = value;
36 }
37
38 Type update(Type value) {
39 Type delta = value - m_value;
40 if (delta > 0 && delta > m_maxDelta) {
41 m_value += delta - m_maxDelta;
42 } else if (delta < 0 && delta < -m_maxDelta) {
43 m_value += delta + m_maxDelta;
44 }
45
46 return m_value;
47 }
48
49 Type value() const { return m_value; }
50
51private:
52 Type m_value;
53 Type m_maxDelta;
54};
55
56/*
57 A point that has its movement dampened.
58 */
59class DampedPointF {
60public:
61 void setMaxDelta(qreal maxDelta) {
62 m_x.setMaxDelta(maxDelta);
63 m_y.setMaxDelta(maxDelta);
64 }
65
66 qreal maxDelta() const { return m_x.maxDelta(); }
67
68 void reset(const QPointF &point) {
69 m_x.reset(point.x());
70 m_y.reset(point.y());
71 }
72
73 void update(const QPointF &point) {
74 m_x.update(point.x());
75 m_y.update(point.y());
76 }
77
78 qreal x() const { return m_x.value(); }
79 qreal y() const { return m_y.value(); }
80private:
81 Damper<qreal> m_x;
82 Damper<qreal> m_y;
83};
84
85#endif // UBUNTU_GESTURES_DAMPER_H
086
=== added file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp'
--- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2013-05-20 13:14:26 +0000
@@ -0,0 +1,292 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "DirectionalDragArea.h"
18
19#include <QtCore/qmath.h>
20
21DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
22 : QQuickItem(parent)
23 , m_status(WaitingForTouch)
24 , m_touchId(-1)
25 , m_direction(DirectionalDragArea::Rightwards)
26 , m_wideningAngle(0)
27 , m_wideningFactor(0)
28 , m_distanceThreshold(0)
29{
30}
31
32DirectionalDragArea::Direction DirectionalDragArea::direction() const
33{
34 return m_direction;
35}
36
37void DirectionalDragArea::setDirection(DirectionalDragArea::Direction direction)
38{
39 if (direction != m_direction) {
40 m_direction = direction;
41 Q_EMIT directionChanged(m_direction);
42 }
43}
44
45void DirectionalDragArea::setMaxDeviation(qreal value)
46{
47 if (m_dampedPos.maxDelta() != value) {
48 m_dampedPos.setMaxDelta(value);
49 Q_EMIT maxDeviationChanged(value);
50 }
51}
52
53qreal DirectionalDragArea::wideningAngle() const
54{
55 return m_wideningAngle;
56}
57
58void DirectionalDragArea::setWideningAngle(qreal angle)
59{
60 if (angle == m_wideningAngle)
61 return;
62
63 m_wideningAngle = angle;
64 m_wideningFactor = qTan(angle * M_PI / 180.0);
65 Q_EMIT wideningAngleChanged(angle);
66}
67
68void DirectionalDragArea::setDistanceThreshold(qreal value)
69{
70 if (m_distanceThreshold != value) {
71 m_distanceThreshold = value;
72 Q_EMIT distanceThresholdChanged(value);
73 }
74}
75
76qreal DirectionalDragArea::distance() const
77{
78 if (directionIsHorizontal()) {
79 return m_previousPos.x() - m_startPos.x();
80 } else {
81 return m_previousPos.y() - m_startPos.y();
82 }
83}
84
85qreal DirectionalDragArea::touchX() const
86{
87 return m_previousPos.x();
88}
89
90qreal DirectionalDragArea::touchY() const
91{
92 return m_previousPos.y();
93}
94
95void DirectionalDragArea::touchEvent(QTouchEvent *event)
96{
97 if (!isEnabled() || !isVisible()) {
98 QQuickItem::touchEvent(event);
99 return;
100 }
101
102 switch (m_status) {
103 case WaitingForTouch:
104 touchEvent_absent(event);
105 break;
106 case Undecided:
107 touchEvent_undecided(event);
108 break;
109 case Recognized:
110 touchEvent_recognized(event);
111 break;
112 default: // Rejected
113 touchEvent_rejected(event);
114 break;
115 }
116}
117
118void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
119{
120 if ((event->touchPointStates() && (Qt::TouchPointPressed || Qt::TouchPointMoved))
121 && event->touchPoints().count() == 1) {
122 m_startPos = event->touchPoints()[0].pos();
123 m_touchId = event->touchPoints()[0].id();
124 m_dampedPos.reset(m_startPos);
125 setStatus(Undecided);
126 setPreviousPos(m_startPos);
127 }
128}
129
130void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
131{
132 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
133 const QPointF &touchPos = touchPoint->pos();
134
135 if (touchPoint->state() == Qt::TouchPointReleased) {
136 // touch has ended before recognition concluded
137 setStatus(WaitingForTouch);
138 return;
139 }
140
141 if (event->touchPointStates().testFlag(Qt::TouchPointPressed)
142 || event->touchPoints().count() > 1) {
143 // multi-finger drags are not accepted
144 setStatus(Rejected);
145 return;
146 }
147
148 m_previousDampedPos.setX(m_dampedPos.x());
149 m_previousDampedPos.setY(m_dampedPos.y());
150 m_dampedPos.update(touchPos);
151
152 if (!pointInsideAllowedArea()) {
153 setStatus(Rejected);
154 return;
155 }
156
157 if (!movingInRightDirection()) {
158 setStatus(Rejected);
159 return;
160 }
161
162 setPreviousPos(touchPos);
163
164 if (movedFarEnough(touchPos)) {
165 setStatus(Recognized);
166 }
167}
168
169void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
170{
171 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
172
173 setPreviousPos(touchPoint->pos());
174
175 if (touchPoint->state() == Qt::TouchPointReleased) {
176 setStatus(WaitingForTouch);
177 }
178}
179
180void DirectionalDragArea::touchEvent_rejected(QTouchEvent *event)
181{
182 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
183
184 if (!touchPoint || touchPoint->state() == Qt::TouchPointReleased) {
185 setStatus(WaitingForTouch);
186 }
187}
188
189const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
190{
191 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
192 const QTouchEvent::TouchPoint *touchPoint = 0;
193 for (int i = 0; i < touchPoints.size(); ++i) {
194 if (touchPoints.at(i).id() == m_touchId) {
195 touchPoint = &touchPoints.at(i);
196 break;
197 }
198 }
199 return touchPoint;
200}
201
202bool DirectionalDragArea::pointInsideAllowedArea() const
203{
204 qreal dX = m_dampedPos.x() - m_startPos.x();
205 qreal dY = m_dampedPos.y() - m_startPos.y();
206
207 switch (m_direction) {
208 case Upwards:
209 return dY <= 0 && qFabs(dX) <= qFabs(dY) * m_wideningFactor;
210 case Downwards:
211 return dY >= 0 && qFabs(dX) <= dY * m_wideningFactor;
212 case Leftwards:
213 return dX <= 0 && qFabs(dY) <= qFabs(dX) * m_wideningFactor;
214 default: // Rightwards:
215 return dX >= 0 && qFabs(dY) <= dX * m_wideningFactor;
216 }
217}
218
219bool DirectionalDragArea::movingInRightDirection() const
220{
221 switch (m_direction) {
222 case Upwards:
223 return m_dampedPos.y() <= m_previousDampedPos.y();
224 case Downwards:
225 return m_dampedPos.y() >= m_previousDampedPos.y();
226 case Leftwards:
227 return m_dampedPos.x() <= m_previousDampedPos.x();
228 default: // Rightwards:
229 return m_dampedPos.x() >= m_previousDampedPos.x();
230 }
231}
232
233bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
234{
235 if (directionIsHorizontal())
236 return qFabs(point.x() - m_startPos.x()) > m_distanceThreshold;
237 else
238 return qFabs(point.y() - m_startPos.y()) > m_distanceThreshold;
239}
240
241void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
242{
243 if (newStatus == m_status)
244 return;
245
246 m_status = newStatus;
247 Q_EMIT statusChanged(m_status);
248
249 switch (newStatus) {
250 case WaitingForTouch:
251 Q_EMIT draggingChanged(false);
252 break;
253 case Undecided:
254 Q_EMIT draggingChanged(true);
255 break;
256 default: // Rejected
257 // no-op
258 break;
259 }
260}
261
262void DirectionalDragArea::setPreviousPos(QPointF point)
263{
264 Q_ASSERT(m_status != Rejected);
265
266 bool xChanged = m_previousPos.x() != point.x();
267 bool yChanged = m_previousPos.y() != point.y();
268
269 m_previousPos = point;
270
271 if (xChanged) {
272 Q_EMIT touchXChanged(point.x());
273 if (directionIsHorizontal())
274 Q_EMIT distanceChanged(distance());
275 }
276
277 if (yChanged) {
278 Q_EMIT touchYChanged(point.y());
279 if (directionIsVertical())
280 Q_EMIT distanceChanged(distance());
281 }
282}
283
284bool DirectionalDragArea::directionIsHorizontal() const
285{
286 return m_direction == Leftwards || m_direction == Rightwards;
287}
288
289bool DirectionalDragArea::directionIsVertical() const
290{
291 return m_direction == Upwards || m_direction == Downwards;
292}
0293
=== added file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h'
--- plugins/Ubuntu/Gestures/DirectionalDragArea.h 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2013-05-20 13:14:26 +0000
@@ -0,0 +1,179 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DIRECTIONAL_DRAG_AREA_H
18#define DIRECTIONAL_DRAG_AREA_H
19
20#include <QtQuick/QQuickItem>
21#include "UbuntuGesturesGlobal.h"
22#include "Damper.h"
23
24/*
25 An area that detects axis-aligned single-finger drag gestures
26
27 If a drag deviates too much from the components' direction recognition will
28 fail. It will also fail if the drag or flick is too short. E.g. a noisy or
29 fidgety click
30
31 See doc/DirectionalDragArea.svg
32 */
33class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {
34 Q_OBJECT
35
36 // The direction in which the gesture should move in order to be recognized.
37 Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged)
38
39 // The distance travelled by the finger along the axis specified by
40 // DirectionalDragArea's direction.
41 Q_PROPERTY(qreal distance READ distance NOTIFY distanceChanged)
42
43 // Position of the touch point performing the drag.
44 Q_PROPERTY(qreal touchX READ touchX NOTIFY touchXChanged)
45 Q_PROPERTY(qreal touchY READ touchY NOTIFY touchYChanged)
46
47 // The current status of the directional drag gesture area.
48 Q_PROPERTY(Status status READ status NOTIFY statusChanged)
49
50 // Whether a drag gesture is taking place (regardless of whether it's a correct
51 // single-finger directional drag or not)
52 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
53
54 /////
55 // stuff that will be set in stone at some point
56
57 // How far the touch point can move away from its expected position before
58 // it causes a rejection in the gesture recognition. This is to compensate
59 // for both noise in the touch input signal and for the natural irregularities
60 // in the finger movement.
61 // Proper value is likely device-specific.
62 Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
63
64 // Widening angle, in degrees
65 // It's roughly the maximum angle a touch point can make relative to the
66 // axis defined by the compoment's direction for it to be recognized as a
67 // directional drag.
68 Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
69 NOTIFY wideningAngleChanged)
70
71 // How far a touch point has to move from its initial position in order for
72 // it to be recognized as a directional drag.
73 Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
74 NOTIFY distanceThresholdChanged)
75
76 //
77 /////
78
79 Q_ENUMS(Direction)
80 Q_ENUMS(Status)
81public:
82 DirectionalDragArea(QQuickItem *parent = 0);
83
84 enum Direction { Rightwards, // Along the positive direction of the X axis
85 Leftwards, // Along the negative direction of the X axis
86 Downwards, // Along the positive direction of the Y axis
87 Upwards }; // Along the negative direction of the Y axis
88 Direction direction() const;
89 void setDirection(Direction);
90
91 // Describes the state of the directional drag gesture.
92 enum Status {
93 // There's no touch point over this area.
94 WaitingForTouch,
95
96 // A touch point has landed on this area but it's not know yet whether it is
97 // performing a drag in the correct direction.
98 Undecided, //Recognizing,
99
100 // There's a touch point in this area and it performed a drag in the correct
101 // direction.
102 //
103 // Once recognized, the gesture state will move back to Absent only once
104 // that touch point ends. The gesture will remain in the Recognized state even if
105 // the touch point starts moving in other directions or halts.
106 Recognized,
107
108 // A gesture was performed but it wasn't a single-touch drag in the correct
109 // direction.
110 // It will remain in this state until there are no more touch points over this
111 // area, at which point it will move to Absent state.
112 Rejected
113 };
114 Status status() const { return m_status; }
115
116 qreal distance() const;
117
118 qreal touchX() const;
119 qreal touchY() const;
120
121 bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
122
123 qreal maxDeviation() const { return m_dampedPos.maxDelta(); }
124 void setMaxDeviation(qreal value);
125
126 qreal wideningAngle() const;
127 void setWideningAngle(qreal value);
128
129 qreal distanceThreshold() const { return m_distanceThreshold; }
130 void setDistanceThreshold(qreal value);
131
132Q_SIGNALS:
133 void directionChanged(Direction direction);
134 void statusChanged(Status value);
135 void draggingChanged(bool value);
136 void distanceChanged(qreal value);
137 void maxDeviationChanged(qreal value);
138 void wideningAngleChanged(qreal value);
139 void distanceThresholdChanged(qreal value);
140 void touchXChanged(qreal value);
141 void touchYChanged(qreal value);
142
143protected:
144 virtual void touchEvent(QTouchEvent *event);
145
146private:
147 void touchEvent_absent(QTouchEvent *event);
148 void touchEvent_undecided(QTouchEvent *event);
149 void touchEvent_recognized(QTouchEvent *event);
150 void touchEvent_rejected(QTouchEvent *event);
151 bool pointInsideAllowedArea() const;
152 bool movingInRightDirection() const;
153 bool movedFarEnough(const QPointF &point) const;
154 const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
155 void setStatus(Status newStatus);
156 void setPreviousPos(QPointF point);
157
158 // convenience functions
159 bool directionIsHorizontal() const;
160 bool directionIsVertical() const;
161
162 Status m_status;
163
164 QPointF m_startPos;
165 QPointF m_previousPos;
166 int m_touchId;
167
168 // A movement damper is used in some of the gesture recognition calculations
169 // to get rid of noise or small oscillations in the touch position.
170 DampedPointF m_dampedPos;
171 QPointF m_previousDampedPos;
172
173 Direction m_direction;
174 qreal m_wideningAngle; // in degrees
175 qreal m_wideningFactor; // it's tan(degreesToRadian(m_wideningAngle))
176 qreal m_distanceThreshold;
177};
178
179#endif // DIRECTIONAL_DRAG_AREA_H
0180
=== added file 'plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h'
--- plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/UbuntuGesturesGlobal.h 2013-05-20 13:14:26 +0000
@@ -0,0 +1,23 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <QtCore/QtGlobal>
18
19#if defined(UBUNTUGESTURES_LIBRARY)
20# define UBUNTUGESTURES_EXPORT Q_DECL_EXPORT
21#else
22# define UBUNTUGESTURES_EXPORT Q_DECL_IMPORT
23#endif
024
=== added file 'plugins/Ubuntu/Gestures/plugin.cpp'
--- plugins/Ubuntu/Gestures/plugin.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/plugin.cpp 2013-05-20 13:14:26 +0000
@@ -0,0 +1,25 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "plugin.h"
18#include "DirectionalDragArea.h"
19
20#include <qqml.h>
21
22void UbuntuGestureQmlPlugin::registerTypes(const char *uri)
23{
24 qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea");
25}
026
=== added file 'plugins/Ubuntu/Gestures/plugin.h'
--- plugins/Ubuntu/Gestures/plugin.h 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/plugin.h 2013-05-20 13:14:26 +0000
@@ -0,0 +1,31 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef PLUGIN_H
18#define PLUGIN_H
19
20#include <QtQml/QQmlExtensionPlugin>
21
22class UbuntuGestureQmlPlugin : public QQmlExtensionPlugin
23{
24 Q_OBJECT
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26public:
27 void registerTypes(const char *uri);
28};
29
30
31#endif
032
=== added file 'plugins/Ubuntu/Gestures/qmldir'
--- plugins/Ubuntu/Gestures/qmldir 1970-01-01 00:00:00 +0000
+++ plugins/Ubuntu/Gestures/qmldir 2013-05-20 13:14:26 +0000
@@ -0,0 +1,2 @@
1module Ubuntu.Gestures
2plugin UbuntuGestureQml
03
=== modified file 'tests/plugins/CMakeLists.txt'
--- tests/plugins/CMakeLists.txt 2013-04-16 14:22:23 +0000
+++ tests/plugins/CMakeLists.txt 2013-05-20 13:14:26 +0000
@@ -1,1 +1,2 @@
1add_subdirectory(Utils)1add_subdirectory(Utils)
2add_subdirectory(Ubuntu)
23
=== added directory 'tests/plugins/Ubuntu'
=== added file 'tests/plugins/Ubuntu/CMakeLists.txt'
--- tests/plugins/Ubuntu/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/CMakeLists.txt 2013-05-20 13:14:26 +0000
@@ -0,0 +1,1 @@
1add_subdirectory(Gestures)
02
=== added directory 'tests/plugins/Ubuntu/Gestures'
=== added file 'tests/plugins/Ubuntu/Gestures/CMakeLists.txt'
--- tests/plugins/Ubuntu/Gestures/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/CMakeLists.txt 2013-05-20 13:14:26 +0000
@@ -0,0 +1,38 @@
1########## tst_DirectionalDragArea
2
3include_directories(
4 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
5 ${CMAKE_CURRENT_BINARY_DIR}
6 )
7
8set(testCommand
9 LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
10 ${CMAKE_CURRENT_BINARY_DIR}/DirectionalDragAreaTestExec
11 -o ${CMAKE_BINARY_DIR}/DirectionalDragAreaTest.xml,xunitxml
12 -o -,txt)
13
14add_custom_target(DirectionalDragAreaTest ${testCommand})
15add_dependencies(qmluitests DirectionalDragAreaTest)
16add_dependencies(DirectionalDragAreaTest DirectionalDragAreaTestExec UbutunGesturesTestQmlFiles)
17
18add_executable(DirectionalDragAreaTestExec tst_DirectionalDragArea.cpp)
19qt5_use_modules(DirectionalDragAreaTestExec Test Core Qml Gui Quick)
20target_link_libraries(DirectionalDragAreaTestExec UbuntuGestureQml)
21
22add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
23
24file(GLOB qmlFiles *.qml)
25add_custom_target(UbuntuGesturesTestQmlFiles ALL
26 COMMAND cp ${qmlFiles} ${CMAKE_CURRENT_BINARY_DIR}
27 DEPENDS ${qmlFiles}
28)
29
30########## tst_Damper
31
32set(damperTestCommand
33 DamperTestExec -o ${CMAKE_BINARY_DIR}/DamperTest.xml,xunitxml -o -,txt)
34
35add_test(NAME DamperTest COMMAND ${damperTestCommand})
36
37add_executable(DamperTestExec tst_Damper.cpp)
38qt5_use_modules(DamperTestExec Test Core)
039
=== added file 'tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml 2013-05-20 13:14:26 +0000
@@ -0,0 +1,87 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Gestures 0.1
19import Ubuntu.Components 0.1
20
21Item {
22
23 function reset() { launcher.y = -launcher.height }
24
25 Rectangle {
26 id: launcher
27 color: "blue"
28 width: parent.width
29 height: units.gu(15)
30 x: 0
31 y: followDragArea()
32
33 function followDragArea() {
34 if (dragArea.status === DirectionalDragArea.Rejected)
35 return -height
36 else
37 return dragArea.distance < height ? -height + dragArea.distance : 0
38 }
39 }
40
41 Rectangle {
42 id: dragAreaRect
43 color: "yellow"
44 opacity: 0.0
45 anchors.fill: dragArea
46 }
47
48 DirectionalDragArea {
49 id: dragArea
50 objectName: "vpDragArea"
51
52 height: units.gu(5)
53
54 direction: DirectionalDragArea.Downwards
55 maxDeviation: units.gu(2)
56 wideningAngle: 10
57 distanceThreshold: units.gu(4)
58
59 onStatusChanged: {
60 switch (status) {
61 case DirectionalDragArea.WaitingForTouch:
62 dragAreaRect.opacity = 0.0
63 break;
64 case DirectionalDragArea.Undecided:
65 dragAreaRect.color = "yellow"
66 dragAreaRect.opacity = 0.3
67 launcher.y = Qt.binding(launcher.followDragArea)
68 break;
69 case DirectionalDragArea.Recognized:
70 dragAreaRect.color = "green"
71 dragAreaRect.opacity = 0.5
72 break;
73 default: //case DirectionalDragArea.Rejected:
74 dragAreaRect.color = "red"
75 dragAreaRect.opacity = 0.5
76 launcher.y = -launcher.height
77 break;
78 }
79 }
80
81 anchors {
82 left: parent.left
83 right: parent.right
84 top: parent.top
85 }
86 }
87}
088
=== added file 'tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml 2013-05-20 13:14:26 +0000
@@ -0,0 +1,90 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Gestures 0.1
19import Ubuntu.Components 0.1
20
21Item {
22 id: root
23
24 function reset() { launcher.x = root.width }
25
26 Rectangle {
27 id: launcher
28 color: "blue"
29 width: units.gu(15)
30 height: parent.height
31 x: root.width
32 y: 0
33
34 function followDragArea() {
35 if (dragArea.status === DirectionalDragArea.Rejected)
36 return root.width
37 else
38 return dragArea.distance > -width ?
39 root.width + dragArea.distance
40 :
41 root.width - width
42 }
43 }
44
45 Rectangle {
46 id: dragAreaRect
47 opacity: 0.0
48 anchors.fill: dragArea
49 }
50
51 DirectionalDragArea {
52 id: dragArea
53 objectName: "hnDragArea"
54
55 width: units.gu(5)
56
57 direction: DirectionalDragArea.Leftwards
58 maxDeviation: units.gu(2)
59 wideningAngle: 10
60 distanceThreshold: units.gu(4)
61
62 onStatusChanged: {
63 switch (status) {
64 case DirectionalDragArea.WaitingForTouch:
65 dragAreaRect.opacity = 0.0
66 break;
67 case DirectionalDragArea.Undecided:
68 dragAreaRect.color = "yellow"
69 dragAreaRect.opacity = 0.3
70 launcher.x = Qt.binding(launcher.followDragArea)
71 break;
72 case DirectionalDragArea.Recognized:
73 dragAreaRect.color = "green"
74 dragAreaRect.opacity = 0.5
75 break;
76 default: //case DirectionalDragArea.Rejected:
77 dragAreaRect.color = "red"
78 dragAreaRect.opacity = 0.5
79 launcher.x = -launcher.height
80 break;
81 }
82 }
83
84 anchors {
85 right: parent.right
86 top: parent.top
87 bottom: parent.bottom
88 }
89 }
90}
091
=== added file 'tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml 2013-05-20 13:14:26 +0000
@@ -0,0 +1,86 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Gestures 0.1
19import Ubuntu.Components 0.1
20
21Item {
22
23 function reset() { launcher.x = -launcher.width }
24
25 Rectangle {
26 id: launcher
27 color: "blue"
28 width: units.gu(15)
29 height: parent.height
30 x: followDragArea()
31 y: 0
32
33 function followDragArea() {
34 if (dragArea.status === DirectionalDragArea.Rejected)
35 return -width
36 else
37 return dragArea.distance < width ? -width + dragArea.distance : 0
38 }
39 }
40
41 Rectangle {
42 id: dragAreaRect
43 opacity: 0.0
44 anchors.fill: dragArea
45 }
46
47 DirectionalDragArea {
48 id: dragArea
49 objectName: "hpDragArea"
50
51 width: units.gu(5)
52
53 direction: DirectionalDragArea.Rightwards
54 maxDeviation: units.gu(2)
55 wideningAngle: 10
56 distanceThreshold: units.gu(4)
57
58 onStatusChanged: {
59 switch (status) {
60 case DirectionalDragArea.WaitingForTouch:
61 dragAreaRect.opacity = 0.0
62 break;
63 case DirectionalDragArea.Undecided:
64 dragAreaRect.color = "yellow"
65 dragAreaRect.opacity = 0.3
66 launcher.x = Qt.binding(launcher.followDragArea)
67 break;
68 case DirectionalDragArea.Recognized:
69 dragAreaRect.color = "green"
70 dragAreaRect.opacity = 0.5
71 break;
72 default: //case DirectionalDragArea.Rejected:
73 dragAreaRect.color = "red"
74 dragAreaRect.opacity = 0.5
75 launcher.x = -launcher.height
76 break;
77 }
78 }
79
80 anchors {
81 left: parent.left
82 top: parent.top
83 bottom: parent.bottom
84 }
85 }
86}
087
=== added file 'tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml'
--- tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml 2013-05-20 13:14:26 +0000
@@ -0,0 +1,90 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Gestures 0.1
19import Ubuntu.Components 0.1
20
21Item {
22 id: root
23
24 function reset() { launcher.y = root.height }
25
26 Rectangle {
27 id: launcher
28 color: "blue"
29 width: parent.width
30 height: units.gu(15)
31 x: 0
32 y: root.height
33
34 function followDragArea() {
35 if (dragArea.status === DirectionalDragArea.Rejected)
36 return root.height
37 else
38 return dragArea.distance > -height ?
39 root.height + dragArea.distance
40 :
41 root.height - height
42 }
43 }
44
45 Rectangle {
46 id: dragAreaRect
47 opacity: 0.0
48 anchors.fill: dragArea
49 }
50
51 DirectionalDragArea {
52 id: dragArea
53 objectName: "vnDragArea"
54
55 height: units.gu(5)
56
57 direction: DirectionalDragArea.Upwards
58 maxDeviation: units.gu(2)
59 wideningAngle: 10
60 distanceThreshold: units.gu(4)
61
62 onStatusChanged: {
63 switch (status) {
64 case DirectionalDragArea.WaitingForTouch:
65 dragAreaRect.opacity = 0.0
66 break;
67 case DirectionalDragArea.Undecided:
68 dragAreaRect.color = "yellow"
69 dragAreaRect.opacity = 0.3
70 launcher.y = Qt.binding(launcher.followDragArea)
71 break;
72 case DirectionalDragArea.Recognized:
73 dragAreaRect.color = "green"
74 dragAreaRect.opacity = 0.5
75 break;
76 default: //case DirectionalDragArea.Rejected:
77 dragAreaRect.color = "red"
78 dragAreaRect.opacity = 0.5
79 launcher.y = -launcher.height
80 break;
81 }
82 }
83
84 anchors {
85 left: parent.left
86 right: parent.right
87 bottom: parent.bottom
88 }
89 }
90}
091
=== added file 'tests/plugins/Ubuntu/Gestures/edgeDragExample.qml'
--- tests/plugins/Ubuntu/Gestures/edgeDragExample.qml 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/edgeDragExample.qml 2013-05-20 13:14:26 +0000
@@ -0,0 +1,40 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19
20Rectangle {
21 //width: units.gu(40)
22 //height: units.gu(71)
23 color: "white"
24
25 MouseArea {
26 id: mouseArea
27 anchors.fill: parent
28 onClicked: {
29 hpLauncher.reset()
30 hnLauncher.reset()
31 vpLauncher.reset()
32 vnLauncher.reset()
33 }
34 }
35
36 RightwardsLauncher { id: hpLauncher; anchors.fill: parent }
37 LeftwardsLauncher { id: hnLauncher; anchors.fill: parent }
38 DownwardsLauncher { id: vpLauncher; anchors.fill: parent }
39 UpwardsLauncher { id: vnLauncher; anchors.fill: parent }
40}
041
=== added file 'tests/plugins/Ubuntu/Gestures/tst_Damper.cpp'
--- tests/plugins/Ubuntu/Gestures/tst_Damper.cpp 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/tst_Damper.cpp 2013-05-20 13:14:26 +0000
@@ -0,0 +1,40 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <QtTest/QtTest>
18
19#include <Damper.h>
20
21class tst_Damper : public QObject
22{
23 Q_OBJECT
24private Q_SLOTS:
25 void negativeMovement();
26};
27
28void tst_Damper::negativeMovement()
29{
30 Damper<qreal> damper;
31
32 damper.setMaxDelta(3.0);
33 damper.reset(0.0);
34 damper.update(-5.0);
35 QCOMPARE(damper.value(), -2.0);
36}
37
38QTEST_MAIN(tst_Damper)
39
40#include "tst_Damper.moc"
041
=== added file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp'
--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 1970-01-01 00:00:00 +0000
+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2013-05-20 13:14:26 +0000
@@ -0,0 +1,275 @@
1/*
2 * Copyright (C) 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <QtTest/QtTest>
18#include <QtCore/QObject>
19#include <qpa/qwindowsysteminterface.h>
20#include <QtQuick/QQuickView>
21#include <QtQml/QQmlEngine>
22
23#include <DirectionalDragArea.h>
24
25class tst_DirectionalDragArea: public QObject
26{
27 Q_OBJECT
28public:
29 tst_DirectionalDragArea() : device(0) { }
30private Q_SLOTS:
31 void initTestCase(); // will be called before the first test function is executed
32 void cleanupTestCase(); // will be called after the last test function was executed.
33
34 void edgeDrag();
35 void edgeDrag_data();
36 void dragWithShortDirectionChange();
37
38private:
39 QQuickView *createView();
40 QTouchDevice *device;
41};
42
43void tst_DirectionalDragArea::initTestCase()
44{
45 if (!device) {
46 device = new QTouchDevice;
47 device->setType(QTouchDevice::TouchScreen);
48 QWindowSystemInterface::registerTouchDevice(device);
49 }
50}
51
52void tst_DirectionalDragArea::cleanupTestCase()
53{
54}
55
56QQuickView *tst_DirectionalDragArea::createView()
57{
58 QQuickView *window = new QQuickView(0);
59 window->setResizeMode(QQuickView::SizeRootObjectToView);
60 window->resize(600, 600);
61 window->engine()->addImportPath(QLatin1String(UBUNTU_GESTURES_PLUGIN_DIR));
62
63 return window;
64}
65
66namespace {
67QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
68{
69 switch (edgeDragArea->direction()) {
70 case DirectionalDragArea::Upwards:
71 return QPointF(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
72 case DirectionalDragArea::Downwards:
73 return QPointF(view->width()/2.0f, edgeDragArea->height()/2.0f);
74 case DirectionalDragArea::Leftwards:
75 return QPointF(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
76 default: // DirectionalDragArea::Rightwards:
77 return QPointF(edgeDragArea->width()/2.0f, view->height()/2.0f);
78 }
79}
80
81QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
82 qreal wideningAngleMultiplier)
83{
84 qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
85 * M_PI / 180.0;
86
87 qreal angleCos = qCos(angleRadians);
88 qreal angleSin = qSin(angleRadians);
89
90 switch (edgeDragArea->direction()) {
91 case DirectionalDragArea::Upwards:
92 return QPointF(angleSin, -angleCos);
93 case DirectionalDragArea::Downwards:
94 return QPointF(angleSin, angleCos);
95 case DirectionalDragArea::Leftwards:
96 return QPointF(-angleCos, angleSin);
97 default: // DirectionalDragArea::Rightwards:
98 return QPointF(angleCos, angleSin);
99 }
100}
101
102QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
103{
104 qreal deviation = edgeDragArea->maxDeviation() * 0.8;
105
106 if (edgeDragArea->direction() == DirectionalDragArea::Leftwards
107 || edgeDragArea->direction() == DirectionalDragArea::Rightwards) {
108 return QPointF(0, deviation);
109 } else {
110 return QPointF(deviation, 0);
111 }
112}
113}
114
115void tst_DirectionalDragArea::edgeDrag()
116{
117 QFETCH(QString, dragAreaObjectName);
118 QFETCH(qreal, wideningAngleMultiplier);
119 QFETCH(qreal, dragDistanceFactor);
120 QFETCH(int, expectedGestureRecognition);
121
122 QQuickView *view = createView();
123 QScopedPointer<QQuickView> scope(view);
124 view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
125 view->show();
126 QVERIFY(QTest::qWaitForWindowExposed(view));
127 QVERIFY(view->rootObject() != 0);
128 qApp->processEvents();
129
130 DirectionalDragArea *edgeDragArea =
131 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
132 QVERIFY(edgeDragArea != 0);
133
134 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
135
136 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
137 QPointF touchPoint = initialTouchPos;
138
139 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
140 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
141 wideningAngleMultiplier);
142 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
143
144 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
145
146 QCOMPARE(draggingSpy.count(), 1);
147 QCOMPARE(edgeDragArea->dragging(), true);
148
149 if (wideningAngleMultiplier > 0) {
150 // go close to the border of the valid area for this touch point
151 // in order to make it easier to leave it by dragging at an angle
152 // slightly bigger than the widening angle
153 touchPoint += createTouchDeviation(edgeDragArea);
154 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
155 }
156
157 do {
158 touchPoint += touchMovement;
159 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
160 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
161
162 if (expectedGestureRecognition)
163 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
164
165 if (edgeDragArea->status() == DirectionalDragArea::Rejected)
166 QCOMPARE(edgeDragArea->dragging(), false);
167
168 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
169
170 QCOMPARE(draggingSpy.count(), 2);
171 QCOMPARE(edgeDragArea->dragging(), false);
172}
173
174void tst_DirectionalDragArea::edgeDrag_data()
175{
176 QTest::addColumn<QString>("dragAreaObjectName");
177 QTest::addColumn<qreal>("wideningAngleMultiplier");
178 QTest::addColumn<qreal>("dragDistanceFactor");
179 QTest::addColumn<int>("expectedGestureRecognition");
180
181 QTest::newRow("rightwards, tiny drag")
182 << "hpDragArea" << 0.0 << 0.2 << 0;
183
184 QTest::newRow("rightwards, straight drag")
185 << "hpDragArea" << 0.0 << 3.0 << 1;
186
187 QTest::newRow("rightwards, diagonal drag")
188 << "hpDragArea" << 0.9 << 3.0 << 1;
189
190 QTest::newRow("rightwards, overly diagonal drag")
191 << "hpDragArea" << 2.0 << 3.0 << 0;
192
193 QTest::newRow("leftwards, tiny drag")
194 << "hnDragArea" << 0.0 << 0.2 << 0;
195
196 QTest::newRow("leftwards, straight drag")
197 << "hnDragArea" << 0.0 << 3.0 << 1;
198
199 QTest::newRow("leftwards, diagonal drag")
200 << "hnDragArea" << 0.9 << 3.0 << 1;
201
202 QTest::newRow("downwards, tiny drag")
203 << "vpDragArea" << 0.0 << 0.2 << 0;
204
205 QTest::newRow("downwards, straight drag")
206 << "vpDragArea" << 0.0 << 3.0 << 1;
207
208 QTest::newRow("downwards, diagonal drag")
209 << "vpDragArea" << 0.9 << 3.0 << 1;
210
211 QTest::newRow("upwards, tiny drag")
212 << "vnDragArea" << 0.0 << 0.2 << 0;
213
214 QTest::newRow("upwards, straight drag")
215 << "vnDragArea" << 0.0 << 3.0 << 1;
216
217 QTest::newRow("upwards, diagonal drag")
218 << "vnDragArea" << 0.9 << 3.0 << 1;
219
220 QTest::newRow("upwards, overly diagonal drag")
221 << "vnDragArea" << 2.0 << 3.0 << 0;
222}
223
224/*
225 A directional drag should still be recognized if there is a momentaneous, small,
226 change in the direction of a drag. That should be accounted as input noise and
227 therefore ignored.
228 */
229void tst_DirectionalDragArea::dragWithShortDirectionChange()
230{
231 QQuickView *view = createView();
232 QScopedPointer<QQuickView> scope(view);
233 view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
234 view->show();
235 QVERIFY(QTest::qWaitForWindowExposed(view));
236 QVERIFY(view->rootObject() != 0);
237 qApp->processEvents();
238
239 DirectionalDragArea *edgeDragArea =
240 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
241 QVERIFY(edgeDragArea != 0);
242
243 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
244 QPointF touchPoint = initialTouchPos;
245
246 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
247 QPointF dragDirectionVector(1.0, 0.0);
248 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
249
250 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
251
252 // Move a bit in the proper direction
253 for (int i=0; i < 3; ++i) {
254 touchPoint += touchMovement;
255 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
256 }
257
258 // Then a sudden and small movement to the opposite direction
259 touchPoint -= dragDirectionVector * (edgeDragArea->maxDeviation() * 0.7);
260 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
261
262 // And then resume movment in the correct direction until it crosses the distance threshold.
263 do {
264 touchPoint += touchMovement;
265 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
266 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
267
268 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
269
270 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
271}
272
273QTEST_MAIN(tst_DirectionalDragArea)
274
275#include "tst_DirectionalDragArea.moc"

Subscribers

People subscribed via source and target branches