on xenial help sign in
Remove DDA use and libs/UbuntuGestures

Some things still need doing/polishing/finishing

commit424c462ad3a5b43975f9bda821dcbe3c9f299762 authorAlbert Astals Cid <albert.astals@canonical.com> dateMon 7 Dec 2015, 11:54 (11 years ago) parent6f193bd
53 files changed+67−5346
CMakeLists.txt−1 libs/CMakeLists.txt−1 libs/UbuntuGestures/CMakeLists.txt−41 libs/UbuntuGestures/CandidateInactivityTimer.cpp−46 libs/UbuntuGestures/CandidateInactivityTimer.h−51 libs/UbuntuGestures/DebugHelpers.cpp−95 libs/UbuntuGestures/DebugHelpers.h−31 libs/UbuntuGestures/Pool.h−124 libs/UbuntuGestures/TimeSource.cpp−49 libs/UbuntuGestures/TimeSource.h−64 libs/UbuntuGestures/Timer.cpp−152 libs/UbuntuGestures/Timer.h−117 libs/UbuntuGestures/TouchOwnershipEvent.cpp−35 libs/UbuntuGestures/TouchOwnershipEvent.h−50 libs/UbuntuGestures/TouchRegistry.cpp−548 libs/UbuntuGestures/TouchRegistry.h−201 libs/UbuntuGestures/UbuntuGesturesGlobal.h−23 libs/UbuntuGestures/UnownedTouchEvent.cpp−39 libs/UbuntuGestures/UnownedTouchEvent.h−45 plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+1−1 plugins/Ubuntu/Gestures/CMakeLists.txt+5−5 plugins/Ubuntu/Gestures/Damper.cpp−24 plugins/Ubuntu/Gestures/Damper.h−89 plugins/Ubuntu/Gestures/DirectionalDragArea.cpp−888 plugins/Ubuntu/Gestures/DirectionalDragArea.h−135 plugins/Ubuntu/Gestures/DirectionalDragArea_p.h−167 plugins/Ubuntu/Gestures/PressedOutsideNotifier.h+1−1 plugins/Ubuntu/Gestures/TouchGate.cpp+2−2 plugins/Ubuntu/Gestures/plugin.cpp+1−3 qml/Components/DragHandle.qml+2−2 qml/Dash/Dash.qml+3−3 qml/Launcher/Launcher.qml+1−1 qml/Panel/IndicatorsMenu.qml+1 qml/Stages/DesktopStage.qml+1−1 qml/Stages/PhoneStage.qml+7−4 qml/Stages/TabletStage.qml+4−4 qml/Tutorial/TutorialBottom.qml+1−1 tests/CMakeLists.txt−1 tests/libs/CMakeLists.txt−1 tests/libs/UbuntuGestures/CMakeLists.txt−16 tests/libs/UbuntuGestures/tst_TouchRegistry.cpp−915 tests/plugins/Ubuntu/Gestures/CMakeLists.txt+5−5 tests/plugins/Ubuntu/Gestures/GestureTest.cpp+2−2 tests/plugins/Ubuntu/Gestures/tst_Damper.cpp−40 tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp−1306 tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+1−1 tests/qmltests/Components/CMakeLists.txt+10−7 tests/qmltests/Components/tst_DragHandle.cpp+1−3 tests/qmltests/Components/tst_EdgeDragEvaluator.cpp+1−1 tests/qmltests/Dash/tst_Dash.qml+7 tests/qmltests/Stages/tst_PhoneStage.qml+3 tests/utils/modules/Unity/Test/CMakeLists.txt+5−2 tests/utils/modules/Unity/Test/testutil.cpp+2−2
CMakeLists.txt−1
@@ -130,7 +130,6 @@ endif()
130130
131131# add subdirectories to build
132132add_subdirectory(include)
133−add_subdirectory(libs)
134133add_subdirectory(src)
135134add_subdirectory(tools)
136135add_subdirectory(qml)
libs/CMakeLists.txtdeleted−1
@@ -1 +0,0 @@
1−add_subdirectory(UbuntuGestures)
libs/UbuntuGestures/CMakeLists.txtdeleted−41
@@ -1,41 +0,0 @@
1−# in order to include Qt's private headers
2−remove_definitions(-DQT_NO_KEYWORDS)
3−
4−set(UbuntuGestures_SOURCES
5− CandidateInactivityTimer.cpp
6− DebugHelpers.cpp
7− Timer.cpp
8− TimeSource.cpp
9− TouchOwnershipEvent.cpp
10− TouchRegistry.cpp
11− UnownedTouchEvent.cpp
12−)
13−
14−add_definitions(-DUBUNTUGESTURES_LIBRARY)
15−
16−add_library(UbuntuGestures SHARED ${UbuntuGestures_SOURCES})
17−
18−qt5_use_modules(UbuntuGestures Core Quick)
19−
20−# So that Foo.cpp can #include "Foo.moc"
21−include_directories(${CMAKE_CURRENT_BINARY_DIR})
22−
23−install(TARGETS UbuntuGestures
24− DESTINATION ${SHELL_PRIVATE_LIBDIR})
25−
26−
27−# There's no cmake var for v8 include path :-/ so create one
28−LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0)
29−if(${Qt5Core_VERSION_STRING} VERSION_LESS "5.1.0")
30− SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/../QtV8/${Qt5Core_VERSION_STRING}/QtV8)
31−else()
32− SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/QtV8/${Qt5Core_VERSION_STRING}/QtV8)
33−endif()
34−
35−# DANGER! DANGER! Using Qt's private API!
36−include_directories(
37− ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
38− ${Qt5Quick_INCLUDE_DIRS}
39− ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
40− ${Qt5V8_PRIVATE_INCLUDE_DIR}
41−)
libs/UbuntuGestures/CandidateInactivityTimer.cppdeleted−46
@@ -1,46 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 "CandidateInactivityTimer.h"
18−
19−namespace UbuntuGestures {
20−
21−CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *candidate,
22− AbstractTimer *timer, QObject *parent)
23− : QObject(parent)
24− , m_timer(timer)
25− , m_touchId(touchId)
26− , m_candidate(candidate)
27−{
28− connect(m_timer, &AbstractTimer::timeout,
29− this, &CandidateInactivityTimer::onTimeout);
30− m_timer->setInterval(durationMs);
31− m_timer->setSingleShot(true);
32− m_timer->start();
33−}
34−
35−CandidateInactivityTimer::~CandidateInactivityTimer()
36−{
37− delete m_timer;
38−}
39−
40−void CandidateInactivityTimer::onTimeout()
41−{
42− qWarning("[TouchRegistry] Candidate for touch %d defaulted!", m_touchId);
43− Q_EMIT candidateDefaulted(m_touchId, m_candidate);
44−}
45−
46−} // namespace UbuntuGestures
libs/UbuntuGestures/CandidateInactivityTimer.hdeleted−51
@@ -1,51 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 UBUNTUGESTURES_CANDIDATE_INACTIVITY_TIMER_H
18−#define UBUNTUGESTURES_CANDIDATE_INACTIVITY_TIMER_H
19−
20−#include <QObject>
21−
22−class QQuickItem;
23−
24−#include "Timer.h"
25−
26−namespace UbuntuGestures {
27−
28−class UBUNTUGESTURES_EXPORT CandidateInactivityTimer : public QObject {
29− Q_OBJECT
30−public:
31− CandidateInactivityTimer(int touchId, QQuickItem *candidate,
32− AbstractTimer *timer,
33− QObject *parent = nullptr);
34−
35− virtual ~CandidateInactivityTimer();
36−
37− const int durationMs = 1000;
38−
39−Q_SIGNALS:
40− void candidateDefaulted(int touchId, QQuickItem *candidate);
41−private Q_SLOTS:
42− void onTimeout();
43−private:
44− AbstractTimer *m_timer;
45− int m_touchId;
46− QQuickItem *m_candidate;
47−};
48−
49−} // namespace UbuntuGestures
50−
51−#endif // UBUNTUGESTURES_CANDIDATE_INACTIVITY_TIMER_H
libs/UbuntuGestures/DebugHelpers.cppdeleted−95
@@ -1,95 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 "DebugHelpers.h"
18−#include <QTouchEvent>
19−#include <QMouseEvent>
20−
21−QString touchPointStateToString(Qt::TouchPointState state)
22−{
23− switch (state) {
24− case Qt::TouchPointPressed:
25− return QStringLiteral("pressed");
26− case Qt::TouchPointMoved:
27− return QStringLiteral("moved");
28− case Qt::TouchPointStationary:
29− return QStringLiteral("stationary");
30− case Qt::TouchPointReleased:
31− return QStringLiteral("released");
32− default:
33− return QStringLiteral("INVALID_STATE");
34− }
35−}
36−
37−QString touchEventToString(const QTouchEvent *ev)
38−{
39− QString message;
40−
41− switch (ev->type()) {
42− case QEvent::TouchBegin:
43− message.append("TouchBegin ");
44− break;
45− case QEvent::TouchUpdate:
46− message.append("TouchUpdate ");
47− break;
48− case QEvent::TouchEnd:
49− message.append("TouchEnd ");
50− break;
51− case QEvent::TouchCancel:
52− message.append("TouchCancel ");
53− break;
54− default:
55− message.append("INVALID_TOUCH_EVENT_TYPE ");
56− }
57−
58− foreach(const QTouchEvent::TouchPoint& touchPoint, ev->touchPoints()) {
59− message.append(
60− QStringLiteral("(id:%1, state:%2, scenePos:(%3,%4)) ")
61− .arg(touchPoint.id())
62− .arg(touchPointStateToString(touchPoint.state()))
63− .arg(touchPoint.scenePos().x())
64− .arg(touchPoint.scenePos().y())
65− );
66− }
67−
68− return message;
69−}
70−
71−QString mouseEventToString(const QMouseEvent *ev)
72−{
73− QString message;
74−
75− switch (ev->type()) {
76− case QEvent::MouseButtonPress:
77− message.append("MouseButtonPress ");
78− break;
79− case QEvent::MouseButtonRelease:
80− message.append("MouseButtonRelease ");
81− break;
82− case QEvent::MouseButtonDblClick:
83− message.append("MouseButtonDblClick ");
84− break;
85− case QEvent::MouseMove:
86− message.append("MouseMove ");
87− break;
88− default:
89− message.append("INVALID_MOUSE_EVENT_TYPE ");
90− }
91−
92− message.append(QStringLiteral("pos(%1, %2)").arg(ev->x()).arg(ev->y()));
93−
94− return message;
95−}
libs/UbuntuGestures/DebugHelpers.hdeleted−31
@@ -1,31 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 UBUNTUGESTURES_DEBUG_HELPER_H
18−#define UBUNTUGESTURES_DEBUG_HELPER_H
19−
20−#include <QString>
21−
22−#include "UbuntuGesturesGlobal.h"
23−
24−class QMouseEvent;
25−class QTouchEvent;
26−
27−UBUNTUGESTURES_EXPORT QString touchPointStateToString(Qt::TouchPointState state);
28−UBUNTUGESTURES_EXPORT QString touchEventToString(const QTouchEvent *ev);
29−UBUNTUGESTURES_EXPORT QString mouseEventToString(const QMouseEvent *ev);
30−
31−#endif // UBUNTUGESTURES_DEBUG_HELPER_H
libs/UbuntuGestures/Pool.hdeleted−124
@@ -1,124 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 UBUNTUGESTURES_POOL_H
18−#define UBUNTUGESTURES_POOL_H
19−
20−#include <QVector>
21−
22−#include "UbuntuGesturesGlobal.h"
23−
24−/*
25− An object pool.
26− Avoids unnecessary creations/initializations and deletions/destructions of items. Useful
27− in a scenario where items are created and destroyed very frequently but the total number
28− of items at any given time remains small. They're stored in a unordered fashion.
29−
30− To be used in Pool, ItemType needs to have the following methods:
31−
32− - ItemType();
33−
34− A constructor that takes no parameters. An object contructed with it must return false if
35− isValid() is called.
36−
37− - bool isValid() const;
38−
39− Returns wheter the object holds a valid , "filled" state or is empty.
40− Used by Pool to check if the slot occupied by this object is actually available.
41−
42− - void reset();
43−
44− Resets the object to its initial, empty, state. After calling this method, isValid() must
45− return false.
46− */
47−template <class ItemType> class Pool
48−{
49−public:
50− Pool() : m_lastUsedIndex(-1) {
51− }
52−
53− class Iterator {
54− public:
55− Iterator() : index(-1), item(nullptr) {}
56− Iterator(int index, ItemType *item)
57− : index(index), item(item) {}
58−
59− ItemType *operator->() const { return item; }
60− ItemType &operator*() const { return *item; }
61− ItemType &value() const { return *item; }
62−
63− operator bool() const { return item != nullptr; }
64−
65− int index;
66− ItemType *item;
67− };
68−
69− ItemType &getEmptySlot() {
70− Q_ASSERT(m_lastUsedIndex < m_slots.size());
71−
72− // Look for an in-between vacancy first
73− for (int i = 0; i < m_lastUsedIndex; ++i) {
74− ItemType &item = m_slots[i];
75− if (!item.isValid()) {
76− return item;
77− }
78− }
79−
80− ++m_lastUsedIndex;
81− if (m_lastUsedIndex >= m_slots.size()) {
82− m_slots.resize(m_lastUsedIndex + 1);
83− }
84−
85− return m_slots[m_lastUsedIndex];
86− }
87−
88− void freeSlot(Iterator &iterator) {
89− m_slots[iterator.index].reset();
90− if (iterator.index == m_lastUsedIndex) {
91− do {
92− --m_lastUsedIndex;
93− } while (m_lastUsedIndex >= 0 && !m_slots.at(m_lastUsedIndex).isValid());
94− }
95− }
96−
97− // Iterates through all valid items (i.e. the occupied slots)
98− // calling the given function, with the option of ending the loop early.
99− //
100− // bool Func(Iterator& item)
101− //
102− // Returning true means it wants to continue the "for" loop, false
103− // terminates the loop.
104− template<typename Func> void forEach(Func func) {
105− Iterator it;
106− for (it.index = 0; it.index <= m_lastUsedIndex; ++it.index) {
107− it.item = &m_slots[it.index];
108− if (!it.item->isValid())
109− continue;
110−
111− if (!func(it))
112− break;
113− }
114− }
115−
116− bool isEmpty() const { return m_lastUsedIndex == -1; }
117−
118−
119−private:
120− QVector<ItemType> m_slots;
121− int m_lastUsedIndex;
122−};
123−
124−#endif // UBUNTUGESTURES_POOL_H
libs/UbuntuGestures/TimeSource.cppdeleted−49
@@ -1,49 +0,0 @@
1−/*
2− * Copyright (C) 2013 - Canonical Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it
5− * under the terms of the GNU Lesser General Public License, as
6− * published by the Free Software Foundation; either version 2.1 or 3.0
7− * of the License.
8− *
9− * This program is distributed in the hope that it will be useful, but
10− * WITHOUT ANY WARRANTY; without even the implied warranties of
11− * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12− * PURPOSE. See the applicable version of the GNU Lesser General Public
13− * License for more details.
14− *
15− * You should have received a copy of both the GNU Lesser General Public
16− * License along with this program. If not, see <http://www.gnu.org/licenses/>
17− *
18− * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
19− */
20−
21−#include "TimeSource.h"
22−
23−#include <QElapsedTimer>
24−
25−namespace UbuntuGestures {
26−class RealTimeSourcePrivate {
27−public:
28− QElapsedTimer timer;
29−};
30−}
31−
32−using namespace UbuntuGestures;
33−
34−RealTimeSource::RealTimeSource()
35− : UbuntuGestures::TimeSource()
36− , d(new RealTimeSourcePrivate)
37−{
38− d->timer.start();
39−}
40−
41−RealTimeSource::~RealTimeSource()
42−{
43− delete d;
44−}
45−
46−qint64 RealTimeSource::msecsSinceReference()
47−{
48− return d->timer.elapsed();
49−}
libs/UbuntuGestures/TimeSource.hdeleted−64
@@ -1,64 +0,0 @@
1−/*
2− * Copyright (C) 2013,2015 Canonical Ltd.
3− *
4− * This program is free software: you can redistribute it and/or modify it
5− * under the terms of the GNU Lesser General Public License, as
6− * published by the Free Software Foundation; either version 2.1 or 3.0
7− * of the License.
8− *
9− * This program is distributed in the hope that it will be useful, but
10− * WITHOUT ANY WARRANTY; without even the implied warranties of
11− * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12− * PURPOSE. See the applicable version of the GNU Lesser General Public
13− * License for more details.
14− *
15− * You should have received a copy of both the GNU Lesser General Public
16− * License along with this program. If not, see <http://www.gnu.org/licenses/>
17− *
18− * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
19− */
20−
21−#ifndef UBUNTUGESTURES_TIMESOURCE_H
22−#define UBUNTUGESTURES_TIMESOURCE_H
23−
24−#include "UbuntuGesturesGlobal.h"
25−#include <QSharedPointer>
26−
27−namespace UbuntuGestures {
28−/*
29− Interface for a time source.
30− */
31−class UBUNTUGESTURES_EXPORT TimeSource {
32−public:
33− virtual ~TimeSource() {}
34− /* Returns the current time in milliseconds since some reference time in the past. */
35− virtual qint64 msecsSinceReference() = 0;
36−};
37−typedef QSharedPointer<TimeSource> SharedTimeSource;
38−
39−/*
40− Implementation of a time source
41− */
42−class RealTimeSourcePrivate;
43−class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
44−public:
45− RealTimeSource();
46− virtual ~RealTimeSource();
47− qint64 msecsSinceReference() override;
48−private:
49− RealTimeSourcePrivate *d;
50−};
51−
52−/*
53− A fake time source, useful for tests
54− */
55−class FakeTimeSource : public TimeSource {
56−public:
57− FakeTimeSource() { m_msecsSinceReference = 0; }
58− qint64 msecsSinceReference() override { return m_msecsSinceReference; }
59− qint64 m_msecsSinceReference;
60−};
61−
62−} // namespace UbuntuGestures
63−
64−#endif // UBUNTUGESTURES_TIMESOURCE_H
libs/UbuntuGestures/Timer.cppdeleted−152
@@ -1,152 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 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 "Timer.h"
18−
19−namespace UbuntuGestures {
20−
21−Timer::Timer(QObject *parent) : AbstractTimer(parent)
22−{
23− m_timer.setSingleShot(false);
24− connect(&m_timer, &QTimer::timeout, this, &AbstractTimer::timeout);
25−}
26−
27−int Timer::interval() const
28−{
29− return m_timer.interval();
30−}
31−
32−void Timer::setInterval(int msecs)
33−{
34− m_timer.setInterval(msecs);
35−}
36−
37−void Timer::start()
38−{
39− m_timer.start();
40− AbstractTimer::start();
41−}
42−
43−void Timer::stop()
44−{
45− m_timer.stop();
46− AbstractTimer::stop();
47−}
48−
49−bool Timer::isSingleShot() const
50−{
51− return m_timer.isSingleShot();
52−}
53−
54−void Timer::setSingleShot(bool value)
55−{
56− m_timer.setSingleShot(value);
57−}
58−
59−/////////////////////////////////// FakeTimer //////////////////////////////////
60−
61−FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
62− : UbuntuGestures::AbstractTimer(parent)
63− , m_interval(0)
64− , m_singleShot(false)
65− , m_timeSource(timeSource)
66−{
67−}
68−
69−void FakeTimer::update()
70−{
71− if (!isRunning()) {
72− return;
73− }
74−
75− if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
76− if (isSingleShot()) {
77− stop();
78− } else {
79− m_nextTimeoutTime += interval();
80− }
81− Q_EMIT timeout();
82− }
83−}
84−
85−void FakeTimer::start()
86−{
87− AbstractTimer::start();
88− m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
89−}
90−
91−int FakeTimer::interval() const
92−{
93− return m_interval;
94−}
95−
96−void FakeTimer::setInterval(int msecs)
97−{
98− m_interval = msecs;
99−}
100−
101−bool FakeTimer::isSingleShot() const
102−{
103− return m_singleShot;
104−}
105−
106−void FakeTimer::setSingleShot(bool value)
107−{
108− m_singleShot = value;
109−}
110−
111−/////////////////////////////////// FakeTimerFactory //////////////////////////////////
112−
113−FakeTimerFactory::FakeTimerFactory()
114−{
115− m_timeSource.reset(new FakeTimeSource);
116−}
117−
118−void FakeTimerFactory::updateTime(qint64 targetTime)
119−{
120− qint64 minTimeoutTime = targetTime;
121−
122− for (int i = 0; i < timers.count(); ++i) {
123− FakeTimer *timer = timers[i].data();
124− if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
125− minTimeoutTime = timer->nextTimeoutTime();
126− }
127− }
128−
129− m_timeSource->m_msecsSinceReference = minTimeoutTime;
130−
131− for (int i = 0; i < timers.count(); ++i) {
132− FakeTimer *timer = timers[i].data();
133− if (timer) {
134− timer->update();
135− }
136− }
137−
138− if (m_timeSource->msecsSinceReference() < targetTime) {
139− updateTime(targetTime);
140− }
141−}
142−
143−AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
144−{
145− FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
146−
147− timers.append(fakeTimer);
148−
149− return fakeTimer;
150−}
151−
152−} // namespace UbuntuGestures
libs/UbuntuGestures/Timer.hdeleted−117
@@ -1,117 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 UBUNTUGESTURES_TIMER_H
18−#define UBUNTUGESTURES_TIMER_H
19−
20−#include "UbuntuGesturesGlobal.h"
21−#include "TimeSource.h"
22−
23−#include <QObject>
24−#include <QPointer>
25−#include <QTimer>
26−
27−namespace UbuntuGestures {
28−
29−/* Defines an interface for a Timer. Useful for tests. */
30−class UBUNTUGESTURES_EXPORT AbstractTimer : public QObject
31−{
32− Q_OBJECT
33−public:
34− AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
35− virtual int interval() const = 0;
36− virtual void setInterval(int msecs) = 0;
37− virtual void start() { m_isRunning = true; }
38− virtual void stop() { m_isRunning = false; }
39− bool isRunning() const { return m_isRunning; }
40− virtual bool isSingleShot() const = 0;
41− virtual void setSingleShot(bool value) = 0;
42−Q_SIGNALS:
43− void timeout();
44−private:
45− bool m_isRunning;
46−};
47−
48−/* Essentially a QTimer wrapper */
49−class UBUNTUGESTURES_EXPORT Timer : public AbstractTimer
50−{
51− Q_OBJECT
52−public:
53− Timer(QObject *parent = nullptr);
54−
55− int interval() const override;
56− void setInterval(int msecs) override;
57− void start() override;
58− void stop() override;
59− bool isSingleShot() const override;
60− void setSingleShot(bool value) override;
61−private:
62− QTimer m_timer;
63−};
64−
65−/* For tests */
66−class UBUNTUGESTURES_EXPORT FakeTimer : public AbstractTimer
67−{
68− Q_OBJECT
69−public:
70− FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
71−
72− void update();
73− qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
74−
75− int interval() const override;
76− void setInterval(int msecs) override;
77− void start() override;
78− bool isSingleShot() const override;
79− void setSingleShot(bool value) override;
80−private:
81− int m_interval;
82− bool m_singleShot;
83− SharedTimeSource m_timeSource;
84− qint64 m_nextTimeoutTime;
85−};
86−
87−class UBUNTUGESTURES_EXPORT AbstractTimerFactory
88−{
89−public:
90− virtual ~AbstractTimerFactory() {}
91− virtual AbstractTimer *createTimer(QObject *parent = nullptr) = 0;
92−};
93−
94−class UBUNTUGESTURES_EXPORT TimerFactory : public AbstractTimerFactory
95−{
96−public:
97− AbstractTimer *createTimer(QObject *parent = nullptr) override { return new Timer(parent); }
98−};
99−
100−class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
101−{
102−public:
103− FakeTimerFactory();
104− virtual ~FakeTimerFactory() {}
105−
106− void updateTime(qint64 msecsSinceReference);
107− QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
108−
109− AbstractTimer *createTimer(QObject *parent = nullptr) override;
110− QList<QPointer<FakeTimer>> timers;
111−private:
112− QSharedPointer<FakeTimeSource> m_timeSource;
113−};
114−
115−} // namespace UbuntuGestures
116−
117−#endif // UBUNTUGESTURES_TIMER_H
libs/UbuntuGestures/TouchOwnershipEvent.cppdeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 "TouchOwnershipEvent.h"
18−
19−QEvent::Type TouchOwnershipEvent::m_touchOwnershipType = (QEvent::Type)-1;
20−
21−TouchOwnershipEvent::TouchOwnershipEvent(int touchId, bool gained)
22− : QEvent(touchOwnershipEventType())
23− , m_touchId(touchId)
24− , m_gained(gained)
25−{
26−}
27−
28−QEvent::Type TouchOwnershipEvent::touchOwnershipEventType()
29−{
30− if (m_touchOwnershipType == (QEvent::Type)-1) {
31− m_touchOwnershipType = (QEvent::Type)registerEventType();
32− }
33−
34− return m_touchOwnershipType;
35−}
libs/UbuntuGestures/TouchOwnershipEvent.hdeleted−50
@@ -1,50 +0,0 @@
1−/*
2− * Copyright (C) 2014 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_TOUCHOWNERSHIPEVENT_H
18−#define UBUNTU_TOUCHOWNERSHIPEVENT_H
19−
20−#include <QEvent>
21−#include "UbuntuGesturesGlobal.h"
22−
23−/*
24− When an item get an ownership event for a touch it can grab/steal that touch
25− with a clean conscience.
26− */
27−class UBUNTUGESTURES_EXPORT TouchOwnershipEvent : public QEvent
28−{
29−public:
30− TouchOwnershipEvent(int touchId, bool gained);
31−
32− static Type touchOwnershipEventType();
33−
34− /*
35− Whether ownership was gained (true) or lost (false)
36− */
37− bool gained() const { return m_gained; }
38−
39− /*
40− Id of the touch whose ownership was granted.
41− */
42− int touchId() const { return m_touchId; }
43−
44−private:
45− static Type m_touchOwnershipType;
46− int m_touchId;
47− bool m_gained;
48−};
49−
50−#endif // UBUNTU_TOUCHOWNERSHIPEVENT_H
libs/UbuntuGestures/TouchRegistry.cppdeleted−548
@@ -1,548 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 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 "TouchRegistry.h"
18−
19−#include <QCoreApplication>
20−#include <QDebug>
21−
22−#pragma GCC diagnostic push
23−#pragma GCC diagnostic ignored "-pedantic"
24−#include <private/qquickitem_p.h>
25−#pragma GCC diagnostic pop
26−
27−#include "CandidateInactivityTimer.h"
28−#include "Timer.h"
29−#include "TouchOwnershipEvent.h"
30−#include "UnownedTouchEvent.h"
31−
32−#define TOUCHREGISTRY_DEBUG 0
33−
34−#if TOUCHREGISTRY_DEBUG
35− #include "DebugHelpers.h"
36− #define UG_DEBUG qDebug() << "[TouchRegistry]"
37−#endif // TOUCHREGISTRY_DEBUG
38−
39−using namespace UbuntuGestures;
40−
41−TouchRegistry *TouchRegistry::m_instance = nullptr;
42−
43−TouchRegistry::TouchRegistry(QObject *parent)
44− : QObject(parent)
45− , m_inDispatchLoop(false)
46− , m_timerFactory(new TimerFactory)
47−{
48−}
49−
50−TouchRegistry::~TouchRegistry()
51−{
52− Q_ASSERT(m_instance != nullptr);
53− m_instance = nullptr;
54− delete m_timerFactory;
55−}
56−
57−TouchRegistry *TouchRegistry::instance()
58−{
59− if (m_instance == nullptr) {
60− m_instance = new TouchRegistry;
61− }
62− return m_instance;
63−}
64−
65−void TouchRegistry::setTimerFactory(AbstractTimerFactory *timerFactory)
66−{
67− delete m_timerFactory;
68− m_timerFactory = timerFactory;
69−}
70−
71−void TouchRegistry::update(const QTouchEvent *event)
72−{
73− #if TOUCHREGISTRY_DEBUG
74− UG_DEBUG << "got" << qPrintable(touchEventToString(event));
75− #endif
76−
77− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
78− for (int i = 0; i < touchPoints.count(); ++i) {
79− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
80− if (touchPoint.state() == Qt::TouchPointPressed) {
81− TouchInfo &touchInfo = m_touchInfoPool.getEmptySlot();
82− touchInfo.init(touchPoint.id());
83− } else if (touchPoint.state() == Qt::TouchPointReleased) {
84− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchPoint.id());
85−
86− touchInfo->physicallyEnded = true;
87− }
88− }
89−
90− deliverTouchUpdatesToUndecidedCandidatesAndWatchers(event);
91−
92− freeEndedTouchInfos();
93−}
94−
95−void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event)
96−{
97− // TODO: Look into how we could optimize this whole thing.
98− // Although it's not really a problem as we should have at most two candidates
99− // for each point and there should not be many active points at any given moment.
100− // But having three nested for-loops does scare.
101−
102− const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
103−
104− // Maps an item to the touches in this event he should be informed about.
105− // E.g.: a QTouchEvent might have three touches but a given item might be interested in only
106− // one of them. So he will get a UnownedTouchEvent from this QTouchEvent containing only that
107− // touch point.
108− QHash<QQuickItem*, QList<int>> touchIdsForItems;
109−
110− // Build touchIdsForItems
111− m_touchInfoPool.forEach([&](Pool<TouchInfo>::Iterator &touchInfo) {
112− if (touchInfo->isOwned() && touchInfo->watchers.isEmpty())
113− return true;
114−
115− for (int j = 0; j < updatedTouchPoints.count(); ++j) {
116− if (updatedTouchPoints[j].id() == touchInfo->id) {
117− if (!touchInfo->isOwned()) {
118− for (int i = 0; i < touchInfo->candidates.count(); ++i) {
119− CandidateInfo &candidate = touchInfo->candidates[i];
120− Q_ASSERT(!candidate.item.isNull());
121− if (candidate.state != CandidateInfo::InterimOwner) {
122− touchIdsForItems[candidate.item.data()].append(touchInfo->id);
123− }
124− }
125− }
126−
127− const QList<QPointer<QQuickItem>> &watchers = touchInfo->watchers;
128− for (int i = 0; i < watchers.count(); ++i) {
129− if (!watchers[i].isNull()) {
130− touchIdsForItems[watchers[i].data()].append(touchInfo->id);
131− }
132− }
133−
134− return true;
135− }
136− }
137−
138− return true;
139− });
140−
141− // TODO: Consider what happens if an item calls any of TouchRegistry's public methods
142− // from the event handler callback.
143− m_inDispatchLoop = true;
144− auto it = touchIdsForItems.constBegin();
145− while (it != touchIdsForItems.constEnd()) {
146− QQuickItem *item = it.key();
147− const QList<int> &touchIds = it.value();
148− dispatchPointsToItem(event, touchIds, item);
149− ++it;
150− };
151− m_inDispatchLoop = false;
152−}
153−
154−void TouchRegistry::freeEndedTouchInfos()
155−{
156− m_touchInfoPool.forEach([&](Pool<TouchInfo>::Iterator &touchInfo) {
157− if (touchInfo->ended()) {
158− m_touchInfoPool.freeSlot(touchInfo);
159− }
160− return true;
161− });
162−}
163−
164−/*
165− Extracts the touches with the given touchIds from event and send them in a
166− UnownedTouchEvent to the given item
167− */
168−void TouchRegistry::dispatchPointsToItem(const QTouchEvent *event, const QList<int> &touchIds,
169− QQuickItem *item)
170−{
171− Qt::TouchPointStates touchPointStates = 0;
172− QList<QTouchEvent::TouchPoint> touchPoints;
173−
174− const QList<QTouchEvent::TouchPoint> &allTouchPoints = event->touchPoints();
175−
176− QTransform windowToCandidateTransform = QQuickItemPrivate::get(item)->windowToItemTransform();
177− QMatrix4x4 windowToCandidateMatrix(windowToCandidateTransform);
178−
179− for (int i = 0; i < allTouchPoints.count(); ++i) {
180− const QTouchEvent::TouchPoint &originalTouchPoint = allTouchPoints[i];
181− if (touchIds.contains(originalTouchPoint.id())) {
182− QTouchEvent::TouchPoint touchPoint = originalTouchPoint;
183−
184− translateTouchPointFromScreenToWindowCoords(touchPoint);
185−
186− // Set the point's local coordinates to that of the item
187− touchPoint.setRect(windowToCandidateTransform.mapRect(touchPoint.sceneRect()));
188− touchPoint.setStartPos(windowToCandidateTransform.map(touchPoint.startScenePos()));
189− touchPoint.setLastPos(windowToCandidateTransform.map(touchPoint.lastScenePos()));
190− touchPoint.setVelocity(windowToCandidateMatrix.mapVector(touchPoint.velocity()).toVector2D());
191−
192− touchPoints.append(touchPoint);
193− touchPointStates |= touchPoint.state();
194− }
195− }
196−
197− QTouchEvent *eventForItem = new QTouchEvent(event->type(),
198− event->device(),
199− event->modifiers(),
200− touchPointStates,
201− touchPoints);
202− eventForItem->setWindow(event->window());
203− eventForItem->setTimestamp(event->timestamp());
204− eventForItem->setTarget(event->target());
205−
206− UnownedTouchEvent unownedTouchEvent(eventForItem);
207−
208− #if TOUCHREGISTRY_DEBUG
209− UG_DEBUG << "Sending unowned" << qPrintable(touchEventToString(eventForItem))
210− << "to" << item;
211− #endif
212−
213− QCoreApplication::sendEvent(item, &unownedTouchEvent);
214−}
215−
216−void TouchRegistry::translateTouchPointFromScreenToWindowCoords(QTouchEvent::TouchPoint &touchPoint)
217−{
218− touchPoint.setScreenRect(touchPoint.sceneRect());
219− touchPoint.setStartScreenPos(touchPoint.startScenePos());
220− touchPoint.setLastScreenPos(touchPoint.lastScenePos());
221−
222− touchPoint.setSceneRect(touchPoint.rect());
223− touchPoint.setStartScenePos(touchPoint.startPos());
224− touchPoint.setLastScenePos(touchPoint.lastPos());
225−}
226−
227−bool TouchRegistry::eventFilter(QObject *watched, QEvent *event)
228−{
229− Q_UNUSED(watched);
230−
231− switch (event->type()) {
232− case QEvent::TouchBegin:
233− case QEvent::TouchUpdate:
234− case QEvent::TouchEnd:
235− case QEvent::TouchCancel:
236− update(static_cast<QTouchEvent*>(event));
237− break;
238− default:
239− // do nothing
240− break;
241− }
242−
243− // Do not filter out the event. i.e., let it be handled further as
244− // we're just monitoring events
245− return false;
246−}
247−
248−void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
249−{
250− #if TOUCHREGISTRY_DEBUG
251− UG_DEBUG << "addCandidateOwnerForTouch id" << id << "candidate" << candidate;
252− #endif
253−
254− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
255− if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
256−
257− if (touchInfo->isOwned()) {
258− qWarning("TouchRegistry: trying to add candidate owner for a touch that's already owned");
259− return;
260− }
261−
262− // TODO: Check if candidate already exists
263−
264− CandidateInfo candidateInfo;
265− candidateInfo.state = CandidateInfo::Undecided;
266− candidateInfo.item = candidate;
267− candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
268− m_timerFactory->createTimer(),
269− this);
270− connect(candidateInfo.inactivityTimer, &CandidateInactivityTimer::candidateDefaulted,
271− this, &TouchRegistry::rejectCandidateOwnerForTouch);
272−
273− touchInfo->candidates.append(candidateInfo);
274−
275− connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
276−}
277−
278−void TouchRegistry::addTouchWatcher(int touchId, QQuickItem *watcher)
279−{
280− #if TOUCHREGISTRY_DEBUG
281− UG_DEBUG << "addTouchWatcher id" << touchId << "watcher" << watcher;
282− #endif
283−
284− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchId);
285− if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
286−
287− // TODO: Check if watcher already exists
288−
289− touchInfo->watchers.append(watcher);
290−}
291−
292−void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
293−{
294− #if TOUCHREGISTRY_DEBUG
295− UG_DEBUG << "removeCandidateOwnerForTouch id" << id << "candidate" << candidate;
296− #endif
297−
298− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
299− if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
300−
301−
302− // TODO: check if the candidate is in fact the owner of the touch
303−
304− bool removed = false;
305− for (int i = 0; i < touchInfo->candidates.count() && !removed; ++i) {
306− if (touchInfo->candidates[i].item == candidate) {
307− removeCandidateOwnerForTouchByIndex(touchInfo, i);
308− removed = true;
309− }
310− }
311−}
312−
313−void TouchRegistry::pruneNullCandidatesForTouch(int touchId)
314−{
315− #if TOUCHREGISTRY_DEBUG
316− UG_DEBUG << "pruneNullCandidatesForTouch touchId" << touchId;
317− #endif
318−
319− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchId);
320− if (!touchInfo) {
321− // doesn't matter as touch is already gone.
322− return;
323− }
324−
325− int i = 0;
326− while (i < touchInfo->candidates.count()) {
327− if (touchInfo->candidates[i].item.isNull()) {
328− removeCandidateOwnerForTouchByIndex(touchInfo, i);
329− } else {
330− ++i;
331− }
332− }
333−}
334−
335−void TouchRegistry::removeCandidateOwnerForTouchByIndex(Pool<TouchRegistry::TouchInfo>::Iterator &touchInfo,
336− int candidateIndex)
337−{
338− // TODO: check if the candidate is in fact the owner of the touch
339−
340− Q_ASSERT(candidateIndex < touchInfo->candidates.count());
341−
342− if (candidateIndex == 0 && touchInfo->candidates[candidateIndex].state != CandidateInfo::Undecided) {
343− qCritical("TouchRegistry: touch owner is being removed.");
344− }
345− removeCandidateHelper(touchInfo, candidateIndex);
346−
347− if (candidateIndex == 0) {
348− // the top candidate has been removed. if the new top candidate
349− // wants the touch let him know he's now the owner.
350− if (touchInfo->isOwned()) {
351− touchInfo->notifyCandidatesOfOwnershipResolution();
352− }
353− }
354−
355− if (!m_inDispatchLoop && touchInfo->ended()) {
356− m_touchInfoPool.freeSlot(touchInfo);
357− }
358−}
359−
360−void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
361−{
362− #if TOUCHREGISTRY_DEBUG
363− UG_DEBUG << "requestTouchOwnership id " << id << "candidate" << candidate;
364− #endif
365−
366− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
367− if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
368−
369− Q_ASSERT(!touchInfo->isOwned());
370−
371− int candidateIndex = -1;
372− for (int i = 0; i < touchInfo->candidates.count(); ++i) {
373− CandidateInfo &candidateInfo = touchInfo->candidates[i];
374− if (candidateInfo.item == candidate) {
375− candidateInfo.state = CandidateInfo::Requested;
376− delete candidateInfo.inactivityTimer;
377− candidateInfo.inactivityTimer = nullptr;
378− candidateIndex = i;
379− break;
380− }
381− }
382−
383− // add it as a candidate if not present yet
384− if (candidateIndex < 0) {
385− CandidateInfo candidateInfo;
386− candidateInfo.state = CandidateInfo::InterimOwner;
387− candidateInfo.item = candidate;
388− candidateInfo.inactivityTimer = nullptr;
389− touchInfo->candidates.append(candidateInfo);
390− // it's the last one
391− candidateIndex = touchInfo->candidates.count() - 1;
392− connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
393− }
394−
395− // If it's the top candidate it means it's now the owner. Let
396− // it know about it.
397− if (candidateIndex == 0) {
398− touchInfo->notifyCandidatesOfOwnershipResolution();
399− }
400−}
401−
402−Pool<TouchRegistry::TouchInfo>::Iterator TouchRegistry::findTouchInfo(int id)
403−{
404− Pool<TouchInfo>::Iterator touchInfo;
405−
406− m_touchInfoPool.forEach([&](Pool<TouchInfo>::Iterator &someTouchInfo) -> bool {
407− if (someTouchInfo->id == id) {
408− touchInfo = someTouchInfo;
409− return false;
410− } else {
411− return true;
412− }
413− });
414−
415− return touchInfo;
416−}
417−
418−
419−void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
420−{
421− // NB: It's technically possible that candidate is a dangling pointer at this point.
422− // Although that would most likely be due to a bug in our code.
423− // In any case, only dereference it after it's confirmed that it indeed exists.
424−
425− #if TOUCHREGISTRY_DEBUG
426− UG_DEBUG << "rejectCandidateOwnerForTouch id" << id << "candidate" << (void*)candidate;
427− #endif
428−
429− Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
430− if (!touchInfo) {
431− #if TOUCHREGISTRY_DEBUG
432− UG_DEBUG << "Failed to find TouchInfo for id" << id;
433− #endif
434− return;
435− }
436−
437− int rejectedCandidateIndex = -1;
438−
439− // Check if the given candidate is valid and still undecided
440− for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
441− CandidateInfo &candidateInfo = touchInfo->candidates[i];
442− if (candidateInfo.item == candidate) {
443− Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
444− if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
445− qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
446− << ") as it already owns touch" << id;
447− return;
448− } else {
449− // we found the guy and it's all fine.
450− rejectedCandidateIndex = i;
451− }
452− }
453− }
454−
455− // If we reached this point it's because the given candidate exists and is indeed undecided.
456−
457− Q_ASSERT(rejectedCandidateIndex >= 0 && rejectedCandidateIndex < touchInfo->candidates.size());
458−
459− {
460− TouchOwnershipEvent lostOwnershipEvent(id, false /*gained*/);
461− QCoreApplication::sendEvent(candidate, &lostOwnershipEvent);
462− }
463−
464− removeCandidateHelper(touchInfo, rejectedCandidateIndex);
465−
466− if (rejectedCandidateIndex == 0) {
467− // the top candidate has been removed. if the new top candidate
468− // wants the touch let him know he's now the owner.
469− if (touchInfo->isOwned()) {
470− touchInfo->notifyCandidatesOfOwnershipResolution();
471− }
472− }
473−}
474−
475−void TouchRegistry::removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex)
476−{
477− {
478− CandidateInfo &candidateInfo = touchInfo->candidates[candidateIndex];
479−
480− delete candidateInfo.inactivityTimer;
481− candidateInfo.inactivityTimer = nullptr;
482−
483− if (candidateInfo.item) {
484− disconnect(candidateInfo.item.data(), nullptr, this, nullptr);
485− }
486− }
487− touchInfo->candidates.removeAt(candidateIndex);
488−}
489−
490−////////////////////////////////////// TouchRegistry::TouchInfo ////////////////////////////////////
491−
492−TouchRegistry::TouchInfo::TouchInfo(int id)
493−{
494− init(id);
495−}
496−
497−void TouchRegistry::TouchInfo::reset()
498−{
499− id = -1;
500−
501− for (int i = 0; i < candidates.count(); ++i) {
502− CandidateInfo &candidate = candidates[i];
503− delete candidate.inactivityTimer;
504− candidate.inactivityTimer.clear(); // shoundn't be needed but anyway...
505− }
506−}
507−
508−void TouchRegistry::TouchInfo::init(int id)
509−{
510− this->id = id;
511− physicallyEnded = false;
512− candidates.clear();
513− watchers.clear();
514−}
515−
516−bool TouchRegistry::TouchInfo::isOwned() const
517−{
518− return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
519−}
520−
521−bool TouchRegistry::TouchInfo::ended() const
522−{
523− Q_ASSERT(isValid());
524− return physicallyEnded && (isOwned() || candidates.isEmpty());
525−}
526−
527−void TouchRegistry::TouchInfo::notifyCandidatesOfOwnershipResolution()
528−{
529− Q_ASSERT(isOwned());
530−
531− #if TOUCHREGISTRY_DEBUG
532− UG_DEBUG << "sending TouchOwnershipEvent(id =" << id
533− << " gained) to candidate" << candidates[0].item;
534− #endif
535−
536− TouchOwnershipEvent gainedOwnershipEvent(id, true /*gained*/);
537− QCoreApplication::sendEvent(candidates[0].item, &gainedOwnershipEvent);
538−
539−
540− TouchOwnershipEvent lostOwnershipEvent(id, false /*gained*/);
541− for (int i = 1; i < candidates.count(); ++i) {
542− #if TOUCHREGISTRY_DEBUG
543− UG_DEBUG << "sending TouchWonershipEvent(id =" << id << " lost) to candidate"
544− << candidates[i].item;
545− #endif
546− QCoreApplication::sendEvent(candidates[i].item, &lostOwnershipEvent);
547− }
548−}
libs/UbuntuGestures/TouchRegistry.hdeleted−201
@@ -1,201 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 UNITY_TOUCHREGISTRY_H
18−#define UNITY_TOUCHREGISTRY_H
19−
20−#include <QQuickItem>
21−#include <QObject>
22−#include <QPointer>
23−#include <QTouchEvent>
24−#include <QVector>
25−
26−#include "UbuntuGesturesGlobal.h"
27−#include "CandidateInactivityTimer.h"
28−#include "Timer.h"
29−#include "Pool.h"
30−
31−namespace UbuntuGestures {
32− class AbstractTimerFactory;
33−}
34−
35−/*
36− Where the ownership of touches is registered.
37−
38− Singleton used for adding a touch point ownership model analogous to the one
39− described in the XInput 2.2 protocol[1] on top of the existing input dispatch logic in QQuickWindow.
40−
41− It provides a much more flexible and powerful way of dealing with pointer ownership than the existing
42− mechanisms in Qt. Namely QQuickItem::grabTouchPoints, QuickItem::keepTouchGrab,
43− QQuickItem::setFiltersChildMouseEvents, QQuickItem::ungrabTouchPoints and QQuickItem::touchUngrabEvent.
44−
45− Usage:
46−
47− 1- An item receives a a new touch point. If he's not sure whether he wants it yet, he calls:
48− TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, this);
49− touchEvent->ignore();
50− Ignoring the event is crucial so that it can be seen by other interested parties, which will
51− behave similarly.
52−
53− 2- That item will then start receiving UnownedTouchEvents for that touch from step 1. Once he's
54− made a decision he calls either:
55− TouchRegistry::instance()->requestTouchOwnership(touchId, this);
56− If he wants the touch point or:
57− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, this);
58−  if he does not want it.
59−
60− Candidates are put in a priority queue. The first one to call addCandidateOwnerForTouch() will
61− take precedence over the others for receiving ownership over the touch point (from now on called
62− simply top-candidate).
63−
64− If the top-candidate calls requestTouchOwnership() he will immediately receive a
65− TouchOwnershipEvent(gained=true) for that touch point. He can then safely call
66− QQuickItem::grabTouchPoints to actually get the owned touch points. The other candidates
67− will receive TouchOwnershipEvent(gained=false) and will no longer receive UnownedTouchEvents
68− for that touch point. They will have to undo whatever action they were performing with that
69− touch point.
70−
71− But if the top-candidate calls removeCandidateOwnerForTouch() instead, he's popped from the
72− candidacy queue and ownership is given to the new top-most candidate if he has already
73− made his decision, that is.
74−
75− The TouchRegistry cannot enforce the results of this pointer ownership negotiation (i.e.,
76− who gets to grab the touch points) as that would clash with QQuickWindow's input event
77− dispatching logic. The candidates have to respect the decision and grab the touch points
78− themselves.
79−
80− If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
81− would be instead:
82− TouchRegistry::instance()->requestTouchOwnership(touchId, this);
83− touchEvent->accept();
84− He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
85− will keep sending touch updates to him already). Eventually he will be notified once ownership has
86− been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
87− other TouchRegistry users wouldn't snatch this touch away from him.
88−
89− Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
90−
91− [1] - http://www.x.org/releases/X11R7.7/doc/inputproto/XI2proto.txt (see multitouch-ownership)
92− */
93−class UBUNTUGESTURES_EXPORT TouchRegistry : public QObject
94−{
95− Q_OBJECT
96−public:
97− virtual ~TouchRegistry();
98−
99− // Returns a pointer to the application's TouchRegistry instance.
100− static TouchRegistry *instance();
101−
102− void update(const QTouchEvent *event);
103−
104− // Calls update() if the given event is a QTouchEvent
105− bool eventFilter(QObject *watched, QEvent *event) override;
106−
107− // An item that might later request ownership over the given touch point.
108− // He will be kept informed about that touch point through UnownedTouchEvents
109− // All candidates must eventually decide whether they want to own the touch point
110− // or not. That decision is informed through requestTouchOwnership() or
111− // removeCandidateOwnerForTouch()
112− void addCandidateOwnerForTouch(int id, QQuickItem *candidate);
113−
114− // The same as rejecting ownership of a touch
115− void removeCandidateOwnerForTouch(int id, QQuickItem *candidate);
116−
117− // The candidate object wants to be the owner of the touch with the given id.
118− // If he's currently the oldest/top-most candidate, he will get an ownership
119− // event immediately. If not, he will get ownership if (or once) he becomes the
120− // top-most candidate.
121− void requestTouchOwnership(int id, QQuickItem *candidate);
122−
123− // An item that has no interest (effective or potential) in owning a touch point
124− // but would nonetheless like to be kept up-to-date on its state.
125− void addTouchWatcher(int touchId, QQuickItem *watcherItem);
126−
127− // Useful for tests, where you should use fake timers
128− void setTimerFactory(UbuntuGestures::AbstractTimerFactory *timerFactory);
129−
130−private Q_SLOTS:
131− void rejectCandidateOwnerForTouch(int id, QQuickItem *candidate);
132−
133−private:
134− // Only instance() can cronstruct one
135− TouchRegistry(QObject *parent = nullptr);
136−
137− class CandidateInfo {
138− public:
139− enum {
140− // A candidate owner that doesn't yet know for sure whether he wants the touch point
141− // (gesture recognition is stilll going on)
142− Undecided = 0,
143− // A candidate owner that wants the touch but hasn't been granted it yet,
144− // most likely because there's an undecided candidate with higher priority
145− Requested = 1,
146− // An item that is the interim owner of the touch, receiving QTouchEvents of it
147− // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
148− // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
149− // yet because of undecided candidates higher up.
150− InterimOwner = 2
151− } state;
152− QPointer<QQuickItem> item;
153− QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
154− };
155−
156− class TouchInfo {
157− public:
158− TouchInfo() : id(-1) {}
159− TouchInfo(int id);
160− bool isValid() const { return id >= 0; }
161− void reset();
162− void init(int id);
163− int id;
164− bool physicallyEnded;
165− bool isOwned() const;
166− bool ended() const;
167− void notifyCandidatesOfOwnershipResolution();
168−
169− // TODO optimize storage (s/QList/Pool)
170− QList<CandidateInfo> candidates;
171− QList<QPointer<QQuickItem>> watchers;
172− };
173−
174− void pruneNullCandidatesForTouch(int touchId);
175− void removeCandidateOwnerForTouchByIndex(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
176− void removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
177−
178− Pool<TouchInfo>::Iterator findTouchInfo(int id);
179−
180− void deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event);
181−
182− static void translateTouchPointFromScreenToWindowCoords(QTouchEvent::TouchPoint &touchPoint);
183−
184− static void dispatchPointsToItem(const QTouchEvent *event, const QList<int> &touchIds,
185− QQuickItem *item);
186− void freeEndedTouchInfos();
187−
188− Pool<TouchInfo> m_touchInfoPool;
189−
190− // the singleton instance
191− static TouchRegistry *m_instance;
192−
193− bool m_inDispatchLoop;
194−
195− UbuntuGestures::AbstractTimerFactory *m_timerFactory;
196−
197− friend class tst_TouchRegistry;
198− friend class tst_DirectionalDragArea;
199−};
200−
201−#endif // UNITY_TOUCHREGISTRY_H
libs/UbuntuGestures/UbuntuGesturesGlobal.hdeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright (C) 2014 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
libs/UbuntuGestures/UnownedTouchEvent.cppdeleted−39
@@ -1,39 +0,0 @@
1−/*
2− * Copyright (C) 2014 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 "UnownedTouchEvent.h"
18−
19−QEvent::Type UnownedTouchEvent::m_unownedTouchEventType = (QEvent::Type)-1;
20−
21−UnownedTouchEvent::UnownedTouchEvent(QTouchEvent *touchEvent)
22− : QEvent(unownedTouchEventType())
23− , m_touchEvent(touchEvent)
24−{
25−}
26−
27−QEvent::Type UnownedTouchEvent::unownedTouchEventType()
28−{
29− if (m_unownedTouchEventType == (QEvent::Type)-1) {
30− m_unownedTouchEventType = (QEvent::Type)registerEventType();
31− }
32−
33− return m_unownedTouchEventType;
34−}
35−
36−QTouchEvent *UnownedTouchEvent::touchEvent()
37−{
38− return m_touchEvent.data();
39−}
libs/UbuntuGestures/UnownedTouchEvent.hdeleted−45
@@ -1,45 +0,0 @@
1−/*
2− * Copyright (C) 2014 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_UNOWNEDTOUCHEVENT_H
18−#define UBUNTU_UNOWNEDTOUCHEVENT_H
19−
20−#include <QTouchEvent>
21−#include <QScopedPointer>
22−#include "UbuntuGesturesGlobal.h"
23−
24−/*
25− A touch event with touch points that do not belong the item receiving it.
26−
27− See TouchRegistry::addCandidateOwnerForTouch and TouchRegistry::addTouchWatcher
28− */
29−class UBUNTUGESTURES_EXPORT UnownedTouchEvent : public QEvent
30−{
31−public:
32− UnownedTouchEvent(QTouchEvent *touchEvent);
33− static Type unownedTouchEventType();
34−
35− // TODO: It might be cleaner to store the information directly in UnownedTouchEvent
36− // instead of carrying around a synthesized QTouchEvent. But the latter option
37− // is very convenient.
38− QTouchEvent *touchEvent();
39−
40−private:
41− static Type m_unownedTouchEventType;
42− QScopedPointer<QTouchEvent> m_touchEvent;
43−};
44−
45−#endif // UBUNTU_UNOWNEDTOUCHEVENT_H
plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+1−1
@@ -24,7 +24,7 @@
2424#include "UbuntuGesturesQmlGlobal.h"
2525#include <stdint.h>
2626#include <QtCore/QObject>
27−#include "TimeSource.h"
27+#include <TimeSource>
2828
2929/*
3030 Estimates the current velocity of a finger based on recent movement along an axis
plugins/Ubuntu/Gestures/CMakeLists.txt+5−5
@@ -4,26 +4,26 @@ remove_definitions(-DQT_NO_KEYWORDS)
44set(UbuntuGesturesQml_SOURCES
55 plugin.cpp
66 AxisVelocityCalculator.cpp
7− Damper.cpp
87 Direction.cpp
9− DirectionalDragArea.cpp
10− FloatingFlickable.cpp
8+# FloatingFlickable.cpp TODO
119 PressedOutsideNotifier.cpp
1210 TouchDispatcher.cpp
1311 TouchGate.cpp
1412)
1513
14+pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
15+
1616add_definitions(-DUBUNTUGESTURESQML_LIBRARY)
1717
1818add_library(UbuntuGesturesQml MODULE ${UbuntuGesturesQml_SOURCES})
19−target_link_libraries(UbuntuGesturesQml UbuntuGestures)
19+target_link_libraries(UbuntuGesturesQml ${UBUNTUGESTURES_LIBRARIES})
2020
2121qt5_use_modules(UbuntuGesturesQml Core Quick)
2222
2323# So that Foo.cpp can #include "Foo.moc"
2424include_directories(${CMAKE_CURRENT_BINARY_DIR})
2525
26−include_directories(${CMAKE_SOURCE_DIR}/libs/UbuntuGestures)
26+include_directories(${UBUNTUGESTURES_INCLUDE_DIRS})
2727
2828# There's no cmake var for v8 include path :-/ so create one
2929LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0)
plugins/Ubuntu/Gestures/Damper.cppdeleted−24
@@ -1,24 +0,0 @@
1−/*
2− * Copyright (C) 2015 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 "Damper.h"
18−#include <QDebug>
19−
20−QDebug operator<<(QDebug dbg, const DampedPointF &p)
21−{
22− dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
23− return dbg.space();
24−}
plugins/Ubuntu/Gestures/Damper.hdeleted−89
@@ -1,89 +0,0 @@
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− */
25−template <class Type> class Damper {
26−public:
27− Damper() : m_value(0), m_maxDelta(0) { }
28−
29− // Maximum delta between the raw value and its dampened counterpart.
30− void setMaxDelta(Type maxDelta) {
31− if (maxDelta < 0) qFatal("Damper::maxDelta must be a positive number.");
32− m_maxDelta = maxDelta;
33− }
34− Type maxDelta() const { return m_maxDelta; }
35−
36− void reset(Type value) {
37− m_value = value;
38− }
39−
40− Type update(Type value) {
41− Type delta = value - m_value;
42− if (delta > 0 && delta > m_maxDelta) {
43− m_value += delta - m_maxDelta;
44− } else if (delta < 0 && delta < -m_maxDelta) {
45− m_value += delta + m_maxDelta;
46− }
47−
48− return m_value;
49− }
50−
51− Type value() const { return m_value; }
52−
53−private:
54− Type m_value;
55− Type m_maxDelta;
56−};
57−
58−/*
59− A point that has its movement dampened.
60− */
61−class DampedPointF {
62−public:
63− void setMaxDelta(qreal maxDelta) {
64− m_x.setMaxDelta(maxDelta);
65− m_y.setMaxDelta(maxDelta);
66− }
67−
68− qreal maxDelta() const { return m_x.maxDelta(); }
69−
70− void reset(const QPointF point) {
71− m_x.reset(point.x());
72− m_y.reset(point.y());
73− }
74−
75− void update(const QPointF point) {
76− m_x.update(point.x());
77− m_y.update(point.y());
78− }
79−
80− qreal x() const { return m_x.value(); }
81− qreal y() const { return m_y.value(); }
82−private:
83− Damper<qreal> m_x;
84− Damper<qreal> m_y;
85−};
86−
87−QDebug operator<<(QDebug dbg, const DampedPointF &p);
88−
89−#endif // UBUNTU_GESTURES_DAMPER_H
plugins/Ubuntu/Gestures/DirectionalDragArea.cppdeleted−888
@@ -1,888 +0,0 @@
1−/*
2− * Copyright (C) 2013-2015 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−#define ACTIVETOUCHESINFO_DEBUG 0
18−#define DIRECTIONALDRAGAREA_DEBUG 0
19−
20−#include "DirectionalDragArea.h"
21−
22−#include <QQuickWindow>
23−#include <QtCore/qmath.h>
24−#include <QScreen>
25−#include <QDebug>
26−
27−#pragma GCC diagnostic push
28−#pragma GCC diagnostic ignored "-pedantic"
29−#include <private/qquickwindow_p.h>
30−#pragma GCC diagnostic pop
31−
32−// local
33−#include "TouchOwnershipEvent.h"
34−#include "TouchRegistry.h"
35−#include "UnownedTouchEvent.h"
36−
37−#include "DirectionalDragArea_p.h"
38−
39−using namespace UbuntuGestures;
40−
41−#if DIRECTIONALDRAGAREA_DEBUG
42−#define ddaDebug(params) qDebug().nospace() << "[DDA(" << qPrintable(objectName()) << ")] " << params
43−#include "DebugHelpers.h"
44−
45−namespace {
46−const char *statusToString(DirectionalDragAreaPrivate::Status status)
47−{
48− if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
49− return "WaitingForTouch";
50− } else if (status == DirectionalDragAreaPrivate::Undecided) {
51− return "Undecided";
52− } else {
53− return "Recognized";
54− }
55−}
56−
57−} // namespace {
58−#else // DIRECTIONALDRAGAREA_DEBUG
59−#define ddaDebug(params) ((void)0)
60−#endif // DIRECTIONALDRAGAREA_DEBUG
61−
62−DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
63− : QQuickItem(parent)
64− , d(new DirectionalDragAreaPrivate(this))
65−{
66− d->setRecognitionTimer(new Timer(this));
67− d->recognitionTimer->setInterval(d->maxTime);
68− d->recognitionTimer->setSingleShot(true);
69−
70− connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
71− connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
72−}
73−
74−Direction::Type DirectionalDragArea::direction() const
75−{
76− return d->direction;
77−}
78−
79−void DirectionalDragArea::setDirection(Direction::Type direction)
80−{
81− if (direction != d->direction) {
82− d->direction = direction;
83− Q_EMIT directionChanged(d->direction);
84− }
85−}
86−
87−void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
88−{
89− if (distanceThreshold != value) {
90− distanceThreshold = value;
91− distanceThresholdSquared = distanceThreshold * distanceThreshold;
92− }
93−}
94−
95−void DirectionalDragAreaPrivate::setMaxTime(int value)
96−{
97− if (maxTime != value) {
98− maxTime = value;
99− recognitionTimer->setInterval(maxTime);
100− }
101−}
102−
103−void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
104−{
105− int interval = 0;
106− bool timerWasRunning = false;
107− bool wasSingleShot = false;
108−
109− // can be null when called from the constructor
110− if (recognitionTimer) {
111− interval = recognitionTimer->interval();
112− timerWasRunning = recognitionTimer->isRunning();
113− if (recognitionTimer->parent() == this) {
114− delete recognitionTimer;
115− }
116− }
117−
118− recognitionTimer = timer;
119− timer->setInterval(interval);
120− timer->setSingleShot(wasSingleShot);
121− connect(timer, &UbuntuGestures::AbstractTimer::timeout,
122− this, &DirectionalDragAreaPrivate::rejectGesture);
123− if (timerWasRunning) {
124− recognitionTimer->start();
125− }
126−}
127−
128−void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
129−{
130− this->timeSource = timeSource;
131− activeTouches.m_timeSource = timeSource;
132−}
133−
134−qreal DirectionalDragArea::distance() const
135−{
136− if (Direction::isHorizontal(d->direction)) {
137− return d->publicPos.x() - d->startPos.x();
138− } else {
139− return d->publicPos.y() - d->startPos.y();
140− }
141−}
142−
143−void DirectionalDragAreaPrivate::updateSceneDistance()
144−{
145− QPointF totalMovement = publicScenePos - startScenePos;
146− sceneDistance = projectOntoDirectionVector(totalMovement);
147−}
148−
149−qreal DirectionalDragArea::sceneDistance() const
150−{
151− return d->sceneDistance;
152−}
153−
154−qreal DirectionalDragArea::touchX() const
155−{
156− return d->publicPos.x();
157−}
158−
159−qreal DirectionalDragArea::touchY() const
160−{
161− return d->publicPos.y();
162−}
163−
164−qreal DirectionalDragArea::touchSceneX() const
165−{
166− return d->publicScenePos.x();
167−}
168−
169−qreal DirectionalDragArea::touchSceneY() const
170−{
171− return d->publicScenePos.y();
172−}
173−
174−bool DirectionalDragArea::dragging() const
175−{
176− return d->status == DirectionalDragAreaPrivate::Recognized;
177−}
178−
179−bool DirectionalDragArea::pressed() const
180−{
181− return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
182−}
183−
184−bool DirectionalDragArea::immediateRecognition() const
185−{
186− return d->immediateRecognition;
187−}
188−
189−void DirectionalDragArea::setImmediateRecognition(bool enabled)
190−{
191− if (d->immediateRecognition != enabled) {
192− d->immediateRecognition = enabled;
193− Q_EMIT immediateRecognitionChanged(enabled);
194− }
195−}
196−
197−void DirectionalDragArea::removeTimeConstraints()
198−{
199− d->setMaxTime(60 * 60 * 1000);
200− d->compositionTime = 0;
201− ddaDebug("removed time constraints");
202−}
203−
204−bool DirectionalDragArea::event(QEvent *event)
205−{
206− if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
207− d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
208− return true;
209− } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
210− d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
211− return true;
212− } else {
213− return QQuickItem::event(event);
214− }
215−}
216−
217−void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
218−{
219− if (event->gained()) {
220− QVector<int> ids;
221− ids.append(event->touchId());
222− ddaDebug("grabbing touch");
223− q->grabTouchPoints(ids);
224−
225− // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
226− // emulation it will cause the emulation logic to go nuts.
227− // Thus we have to also grab the mouse in this case.
228− //
229− // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
230− // TODO: Remove this workaround once we start using Qt 5.4
231− if (q->window()) {
232− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
233− if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
234− ddaDebug("removing mouse grabber");
235− q->window()->mouseGrabberItem()->ungrabMouse();
236− }
237− }
238− } else {
239− // We still wanna know when it ends for keeping the composition time window up-to-date
240− TouchRegistry::instance()->addTouchWatcher(touchId, q);
241−
242− setStatus(WaitingForTouch);
243− }
244−}
245−
246−void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
247−{
248− QTouchEvent *event = unownedTouchEvent->touchEvent();
249−
250− Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
251−
252− ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
253−
254− switch (status) {
255− case WaitingForTouch:
256− // do nothing
257− break;
258− case Undecided:
259− Q_ASSERT(q->isEnabled() && q->isVisible());
260− unownedTouchEvent_undecided(unownedTouchEvent);
261− break;
262− default: // Recognized:
263− // do nothing
264− break;
265− }
266−
267− activeTouches.update(event);
268−}
269−
270−void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
271−{
272− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
273− if (!touchPoint) {
274− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
275− << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
276− "Considering it as released.";
277−
278− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
279− setStatus(WaitingForTouch);
280− return;
281− }
282−
283− const QPointF &touchScenePos = touchPoint->scenePos();
284−
285− if (touchPoint->state() == Qt::TouchPointReleased) {
286− // touch has ended before recognition concluded
287− ddaDebug("Touch has ended before recognition concluded");
288− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
289− setStatus(WaitingForTouch);
290− return;
291− }
292−
293− previousDampedScenePos.setX(dampedScenePos.x());
294− previousDampedScenePos.setY(dampedScenePos.y());
295− dampedScenePos.update(touchScenePos);
296−
297− if (!movingInRightDirection()) {
298− ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
299− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
300− // We still wanna know when it ends for keeping the composition time window up-to-date
301− TouchRegistry::instance()->addTouchWatcher(touchId, q);
302− setStatus(WaitingForTouch);
303− return;
304− }
305−
306− if (isWithinTouchCompositionWindow()) {
307− // There's still time for some new touch to appear and ruin our party as it would be combined
308− // with our touchId one and therefore deny the possibility of a single-finger gesture.
309− ddaDebug("Sill within composition window. Let's wait more.");
310− return;
311− }
312−
313− if (movedFarEnoughAlongGestureAxis()) {
314− TouchRegistry::instance()->requestTouchOwnership(touchId, q);
315− setStatus(Recognized);
316− setPublicPos(touchPoint->pos());
317− setPublicScenePos(touchScenePos);
318− } else if (isPastMaxDistance()) {
319− ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
320− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
321− // We still wanna know when it ends for keeping the composition time window up-to-date
322− TouchRegistry::instance()->addTouchWatcher(touchId, q);
323− setStatus(WaitingForTouch);
324− } else {
325− ddaDebug("Didn't move far enough yet. Let's wait more.");
326− }
327−}
328−
329−void DirectionalDragArea::touchEvent(QTouchEvent *event)
330−{
331− // TODO: Consider when more than one touch starts in the same event (although it's not possible
332− // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
333−
334− ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
335−
336− if (!isEnabled() || !isVisible()) {
337− QQuickItem::touchEvent(event);
338− return;
339− }
340−
341− switch (d->status) {
342− case DirectionalDragAreaPrivate::WaitingForTouch:
343− d->touchEvent_absent(event);
344− break;
345− case DirectionalDragAreaPrivate::Undecided:
346− d->touchEvent_undecided(event);
347− break;
348− default: // Recognized:
349− d->touchEvent_recognized(event);
350− break;
351− }
352−
353− d->activeTouches.update(event);
354−}
355−
356−void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
357−{
358− // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
359−
360− if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
361− // Nothing to see here. No touch starting in this event.
362− return;
363− }
364−
365− // to be proven wrong, if that's the case
366− bool allGood = true;
367−
368− if (isWithinTouchCompositionWindow()) {
369− // too close to the last touch start. So we consider them as starting roughly at the same time.
370− // Can't be a single-touch gesture.
371− ddaDebug("A new touch point came in but we're still within time composition window. Ignoring it.");
372− allGood = false;
373− }
374−
375− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
376−
377− const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
378− for (int i = 0; i < touchPoints.count() && allGood; ++i) {
379− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
380− if (touchPoint.state() == Qt::TouchPointPressed) {
381− if (newTouchPoint) {
382− // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
383− allGood = false;
384− } else {
385− // that's our candidate
386− newTouchPoint = &touchPoint;
387− }
388− }
389− }
390−
391− if (allGood) {
392− allGood = sanityCheckRecognitionProperties();
393− if (!allGood) {
394− qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
395− " is impossible");
396− }
397− }
398−
399− if (allGood) {
400− Q_ASSERT(newTouchPoint);
401−
402− startPos = newTouchPoint->pos();
403− startScenePos = newTouchPoint->scenePos();
404− touchId = newTouchPoint->id();
405− dampedScenePos.reset(startScenePos);
406− setPublicPos(startPos);
407−
408− setPublicScenePos(startScenePos);
409− updateSceneDirectionVector();
410−
411− if (recognitionIsDisabled()) {
412− // Behave like a dumb TouchArea
413− ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
414− TouchRegistry::instance()->requestTouchOwnership(touchId, q);
415− setStatus(Recognized);
416− event->accept();
417− } else {
418− // just monitor the touch points for now.
419− TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
420−
421− setStatus(Undecided);
422− // Let the item below have it. We will monitor it and grab it later if a gesture
423− // gets recognized.
424− event->ignore();
425− }
426− } else {
427− watchPressedTouchPoints(touchPoints);
428− event->ignore();
429− }
430−}
431−
432−void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
433−{
434− Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
435−
436− // We're not interested in new touch points. We already have our candidate (touchId).
437− // But we do want to know when those new touches end for keeping the composition time
438− // window up-to-date
439− event->ignore();
440− watchPressedTouchPoints(event->touchPoints());
441−
442− if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
443− // multi-finger drags are not accepted
444− ddaDebug("Multi-finger drags are not accepted");
445−
446− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
447− // We still wanna know when it ends for keeping the composition time window up-to-date
448− TouchRegistry::instance()->addTouchWatcher(touchId, q);
449−
450− setStatus(WaitingForTouch);
451− }
452−}
453−
454−void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
455−{
456− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
457−
458− if (!touchPoint) {
459− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
460− << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
461− "Considering it as released.";
462− setStatus(WaitingForTouch);
463− } else {
464− setPublicPos(touchPoint->pos());
465− setPublicScenePos(touchPoint->scenePos());
466−
467− if (touchPoint->state() == Qt::TouchPointReleased) {
468− setStatus(WaitingForTouch);
469− }
470− }
471−}
472−
473−void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
474−{
475− for (int i = 0; i < touchPoints.count(); ++i) {
476− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
477− if (touchPoint.state() == Qt::TouchPointPressed) {
478− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
479− }
480− }
481−}
482−
483−bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
484−{
485− return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
486−}
487−
488−bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
489−{
490− return recognitionIsDisabled()
491− || (distanceThreshold < maxDistance && compositionTime < maxTime);
492−}
493−
494−const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
495−{
496− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
497− const QTouchEvent::TouchPoint *touchPoint = 0;
498− for (int i = 0; i < touchPoints.size(); ++i) {
499− if (touchPoints.at(i).id() == touchId) {
500− touchPoint = &touchPoints.at(i);
501− break;
502− }
503− }
504− return touchPoint;
505−}
506−
507−bool DirectionalDragAreaPrivate::movingInRightDirection() const
508−{
509− if (direction == Direction::Horizontal || direction == Direction::Vertical) {
510− return true;
511− } else {
512− QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
513− dampedScenePos.y() - previousDampedScenePos.y());
514−
515− qreal scalarProjection = projectOntoDirectionVector(movementVector);
516−
517− return scalarProjection >= 0.;
518− }
519−}
520−
521−bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
522−{
523− if (distanceThreshold <= 0.) {
524− // distance threshold check is disabled
525− return true;
526− } else {
527− QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
528− dampedScenePos.y() - startScenePos.y());
529−
530− qreal scalarProjection = projectOntoDirectionVector(totalMovement);
531−
532− ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
533− << ", distanceThreshold=" << distanceThreshold);
534−
535− if (direction == Direction::Horizontal || direction == Direction::Vertical) {
536− return qAbs(scalarProjection) > distanceThreshold;
537− } else {
538− return scalarProjection > distanceThreshold;
539− }
540− }
541−}
542−
543−bool DirectionalDragAreaPrivate::isPastMaxDistance() const
544−{
545− QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
546− dampedScenePos.y() - startScenePos.y());
547−
548− qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
549− return squaredDistance > maxDistance*maxDistance;
550−}
551−
552−void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
553−{
554− if (!q->isEnabled() || !q->isVisible()) {
555− if (status == Undecided) {
556− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
557− // We still wanna know when it ends for keeping the composition time window up-to-date
558− TouchRegistry::instance()->addTouchWatcher(touchId, q);
559− }
560−
561− if (status != WaitingForTouch) {
562− ddaDebug("Resetting status because got disabled or made invisible");
563− setStatus(WaitingForTouch);
564− }
565− }
566−}
567−
568−void DirectionalDragAreaPrivate::rejectGesture()
569−{
570− if (status == Undecided) {
571− ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
572−
573− TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
574− // We still wanna know when it ends for keeping the composition time window up-to-date
575− TouchRegistry::instance()->addTouchWatcher(touchId, q);
576−
577− setStatus(WaitingForTouch);
578− }
579−}
580−
581−void DirectionalDragAreaPrivate::setStatus(Status newStatus)
582−{
583− if (newStatus == status)
584− return;
585−
586− Status oldStatus = status;
587−
588− if (oldStatus == Undecided) {
589− recognitionTimer->stop();
590− }
591−
592− status = newStatus;
593− Q_EMIT statusChanged(status);
594−
595− ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
596−
597− switch (newStatus) {
598− case WaitingForTouch:
599− if (oldStatus == Recognized) {
600− Q_EMIT q->draggingChanged(false);
601− }
602− Q_EMIT q->pressedChanged(false);
603− break;
604− case Undecided:
605− recognitionTimer->start();
606− Q_EMIT q->pressedChanged(true);
607− break;
608− case Recognized:
609− Q_EMIT q->draggingChanged(true);
610− break;
611− default:
612− // no-op
613− break;
614− }
615−}
616−
617−void DirectionalDragAreaPrivate::setPublicPos(const QPointF point)
618−{
619− bool xChanged = publicPos.x() != point.x();
620− bool yChanged = publicPos.y() != point.y();
621−
622− // Public position should not get updated while the gesture is still being recognized
623− // (ie, Undecided status).
624− Q_ASSERT(status == WaitingForTouch || status == Recognized);
625−
626− if (status == Recognized && !recognitionIsDisabled()) {
627− // When the gesture finally gets recognized, the finger will likely be
628− // reasonably far from the edge. If we made the contentX immediately
629− // follow the finger position it would be visually unpleasant as it
630− // would appear right next to the user's finger out of nowhere (ie,
631− // it would jump). Instead, we make contentX go towards the user's
632− // finger in several steps. ie., in an animated way.
633− QPointF delta = point - publicPos;
634− // the trick is not to go all the way (1.0) as it would cause a sudden jump
635− publicPos.rx() += 0.4 * delta.x();
636− publicPos.ry() += 0.4 * delta.y();
637− } else {
638− // no smoothing when initializing or if gesture recognition was immediate as there will
639− // be no jump.
640− publicPos = point;
641− }
642−
643− if (xChanged) {
644− Q_EMIT q->touchXChanged(publicPos.x());
645− if (Direction::isHorizontal(direction))
646− Q_EMIT q->distanceChanged(q->distance());
647− }
648−
649− if (yChanged) {
650− Q_EMIT q->touchYChanged(publicPos.y());
651− if (Direction::isVertical(direction))
652− Q_EMIT q->distanceChanged(q->distance());
653− }
654−}
655−
656−void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF point)
657−{
658− bool xChanged = publicScenePos.x() != point.x();
659− bool yChanged = publicScenePos.y() != point.y();
660−
661− if (!xChanged && !yChanged)
662− return;
663−
664− // Public position should not get updated while the gesture is still being recognized
665− // (ie, Undecided status).
666− Q_ASSERT(status == WaitingForTouch || status == Recognized);
667−
668− qreal oldSceneDistance = sceneDistance;
669−
670− if (status == Recognized && !recognitionIsDisabled()) {
671− // When the gesture finally gets recognized, the finger will likely be
672− // reasonably far from the edge. If we made the contentX immediately
673− // follow the finger position it would be visually unpleasant as it
674− // would appear right next to the user's finger out of nowhere (ie,
675− // it would jump). Instead, we make contentX go towards the user's
676− // finger in several steps. ie., in an animated way.
677− QPointF delta = point - publicScenePos;
678− // the trick is not to go all the way (1.0) as it would cause a sudden jump
679− publicScenePos.rx() += 0.4 * delta.x();
680− publicScenePos.ry() += 0.4 * delta.y();
681− } else {
682− // no smoothing when initializing or if gesture recognition was immediate as there will
683− // be no jump.
684− publicScenePos = point;
685− }
686−
687− updateSceneDistance();
688−
689− if (oldSceneDistance != sceneDistance) {
690− Q_EMIT q->sceneDistanceChanged(sceneDistance);
691− }
692−
693− if (xChanged) {
694− Q_EMIT q->touchSceneXChanged(publicScenePos.x());
695− }
696−
697− if (yChanged) {
698− Q_EMIT q->touchSceneYChanged(publicScenePos.y());
699− }
700−}
701−
702−bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
703−{
704− return
705− compositionTime > 0 &&
706− !activeTouches.isEmpty() &&
707− timeSource->msecsSinceReference() <=
708− activeTouches.mostRecentStartTime() + (qint64)compositionTime;
709−}
710−
711−void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
712−{
713− if (change == QQuickItem::ItemSceneChange) {
714− if (value.window != nullptr) {
715− value.window->installEventFilter(TouchRegistry::instance());
716−
717− // TODO: Handle window->screen() changes (ie window changing screens)
718− qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
719− d->setPixelsPerMm(pixelsPerMm);
720− }
721− }
722−}
723−
724−void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
725−{
726− dampedScenePos.setMaxDelta(1. * pixelsPerMm);
727− setDistanceThreshold(4. * pixelsPerMm);
728− maxDistance = 10. * pixelsPerMm;
729−}
730−
731−//************************** ActiveTouchesInfo **************************
732−
733−ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
734− : m_timeSource(timeSource)
735−{
736−}
737−
738−void ActiveTouchesInfo::update(QTouchEvent *event)
739−{
740− if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
741− // nothing to update
742− #if ACTIVETOUCHESINFO_DEBUG
743− qDebug("[DDA::ActiveTouchesInfo] Nothing to Update");
744− #endif
745− return;
746− }
747−
748− const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
749− for (int i = 0; i < touchPoints.count(); ++i) {
750− const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
751− if (touchPoint.state() == Qt::TouchPointPressed) {
752− addTouchPoint(touchPoint.id());
753− } else if (touchPoint.state() == Qt::TouchPointReleased) {
754− removeTouchPoint(touchPoint.id());
755− }
756− }
757−}
758−
759−#if ACTIVETOUCHESINFO_DEBUG
760−QString ActiveTouchesInfo::toString()
761−{
762− QString string = "(";
763−
764− {
765− QTextStream stream(&string);
766− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
767− stream << "(id=" << touchInfo->id << ",startTime=" << touchInfo->startTime << ")";
768− return true;
769− });
770− }
771−
772− string.append(")");
773−
774− return string;
775−}
776−#endif // ACTIVETOUCHESINFO_DEBUG
777−
778−void ActiveTouchesInfo::addTouchPoint(int touchId)
779−{
780− ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
781− activeTouchInfo.id = touchId;
782− activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
783−
784− #if ACTIVETOUCHESINFO_DEBUG
785− qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
786− #endif
787−}
788−
789−qint64 ActiveTouchesInfo::touchStartTime(int touchId)
790−{
791− qint64 result = -1;
792−
793− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
794− if (touchId == touchInfo->id) {
795− result = touchInfo->startTime;
796− return false;
797− } else {
798− return true;
799− }
800− });
801−
802− Q_ASSERT(result != -1);
803− return result;
804−}
805−
806−void ActiveTouchesInfo::removeTouchPoint(int touchId)
807−{
808− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
809− if (touchId == touchInfo->id) {
810− m_touchInfoPool.freeSlot(touchInfo);
811− return false;
812− } else {
813− return true;
814− }
815− });
816−
817− #if ACTIVETOUCHESINFO_DEBUG
818− qDebug() << "[DDA::ActiveTouchesInfo]" << qPrintable(toString());
819− #endif
820−}
821−
822−qint64 ActiveTouchesInfo::mostRecentStartTime()
823−{
824− Q_ASSERT(!m_touchInfoPool.isEmpty());
825−
826− qint64 highestStartTime = -1;
827−
828− m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &activeTouchInfo) {
829− if (activeTouchInfo->startTime > highestStartTime) {
830− highestStartTime = activeTouchInfo->startTime;
831− }
832− return true;
833− });
834−
835− return highestStartTime;
836−}
837−
838−void DirectionalDragAreaPrivate::updateSceneDirectionVector()
839−{
840− QPointF localOrigin(0., 0.);
841− QPointF localDirection;
842− switch (direction) {
843− case Direction::Upwards:
844− localDirection.rx() = 0.;
845− localDirection.ry() = -1.;
846− break;
847− case Direction::Downwards:
848− case Direction::Vertical:
849− localDirection.rx() = 0.;
850− localDirection.ry() = 1;
851− break;
852− case Direction::Leftwards:
853− localDirection.rx() = -1.;
854− localDirection.ry() = 0.;
855− break;
856− default: // Direction::Rightwards || Direction.Horizontal
857− localDirection.rx() = 1.;
858− localDirection.ry() = 0.;
859− break;
860− }
861− QPointF sceneOrigin = q->mapToScene(localOrigin);
862− QPointF sceneDirection = q->mapToScene(localDirection);
863− sceneDirectionVector = sceneDirection - sceneOrigin;
864−}
865−
866−qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF sceneVector) const
867−{
868− // same as dot product as sceneDirectionVector is a unit vector
869− return sceneVector.x() * sceneDirectionVector.x() +
870− sceneVector.y() * sceneDirectionVector.y();
871−}
872−
873−DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
874− : q(q)
875− , status(WaitingForTouch)
876− , sceneDistance(0)
877− , touchId(-1)
878− , direction(Direction::Rightwards)
879− , distanceThreshold(0)
880− , distanceThresholdSquared(0.)
881− , maxTime(400)
882− , compositionTime(60)
883− , immediateRecognition(false)
884− , recognitionTimer(nullptr)
885− , timeSource(new RealTimeSource)
886− , activeTouches(timeSource)
887−{
888−}
plugins/Ubuntu/Gestures/DirectionalDragArea.hdeleted−135
@@ -1,135 +0,0 @@
1−/*
2− * Copyright (C) 2013,2014 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 "UbuntuGesturesQmlGlobal.h"
22−#include "Damper.h"
23−#include "Direction.h"
24−
25−// lib UbuntuGestures
26−#include <Pool.h>
27−#include <Timer.h>
28−
29−class TouchOwnershipEvent;
30−class UnownedTouchEvent;
31−class DirectionalDragAreaPrivate;
32−
33−/*
34− An area that detects axis-aligned single-finger drag gestures
35−
36− If a drag deviates too much from the components' direction recognition will
37− fail. It will also fail if the drag or flick is too short. E.g. a noisy or
38− fidgety click
39−
40− See doc/DirectionalDragArea.svg
41− */
42−class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
43− Q_OBJECT
44−
45− // The direction in which the gesture should move in order to be recognized.
46− Q_PROPERTY(Direction::Type direction READ direction WRITE setDirection NOTIFY directionChanged)
47−
48− // The distance travelled by the finger along the axis specified by
49− // DirectionalDragArea's direction.
50− Q_PROPERTY(qreal distance READ distance NOTIFY distanceChanged)
51−
52− // The distance travelled by the finger along the axis specified by
53− // DirectionalDragArea's direction in scene coordinates
54− Q_PROPERTY(qreal sceneDistance READ sceneDistance NOTIFY sceneDistanceChanged)
55−
56− // Position of the touch point performing the drag relative to this item.
57− Q_PROPERTY(qreal touchX READ touchX NOTIFY touchXChanged)
58− Q_PROPERTY(qreal touchY READ touchY NOTIFY touchYChanged)
59−
60− // Position of the touch point performing the drag, in scene's coordinate system
61− Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
62− Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
63−
64− // Whether a drag gesture is taking place
65− Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
66−
67− // Whether the drag area is pressed.
68− Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
69−
70− // Whether a gesture should be Recognized as soon a touch lands on the area.
71− // With this property enabled it will work pretty much like a MultiPointTouchArea,
72− // just with a different API.
73− //
74− // It's false by default. In most cases you will not want that enabled.
75− Q_PROPERTY(bool immediateRecognition
76− READ immediateRecognition
77− WRITE setImmediateRecognition
78− NOTIFY immediateRecognitionChanged)
79−
80− Q_ENUMS(Direction)
81−public:
82− DirectionalDragArea(QQuickItem *parent = 0);
83−
84− Direction::Type direction() const;
85− void setDirection(Direction::Type);
86−
87− qreal distance() const;
88− qreal sceneDistance() const;
89−
90− qreal touchX() const;
91− qreal touchY() const;
92−
93− qreal touchSceneX() const;
94− qreal touchSceneY() const;
95−
96− bool dragging() const;
97−
98− bool pressed() const;
99−
100− bool immediateRecognition() const;
101− void setImmediateRecognition(bool enabled);
102−
103− bool event(QEvent *e) override;
104−
105− /*
106− In qmltests, sequences of touch events are sent all at once, unlike in "real life".
107− Also qmltests might run really slowly, e.g. when run from inside virtual machines.
108− Thus to remove a variable that qmltests cannot really control, namely time, this
109− function removes all constraints that are sensible to elapsed time.
110−
111− This effectively makes the DirectionalDragArea easier to fool.
112− */
113− Q_INVOKABLE void removeTimeConstraints();
114−
115−Q_SIGNALS:
116− void directionChanged(Direction::Type direction);
117− void draggingChanged(bool value);
118− void pressedChanged(bool value);
119− void distanceChanged(qreal value);
120− void sceneDistanceChanged(qreal value);
121− void touchXChanged(qreal value);
122− void touchYChanged(qreal value);
123− void touchSceneXChanged(qreal value);
124− void touchSceneYChanged(qreal value);
125− void immediateRecognitionChanged(bool value);
126−
127−protected:
128− void touchEvent(QTouchEvent *event) override;
129− void itemChange(ItemChange change, const ItemChangeData &value) override;
130−
131−public: // so tests can access it
132− DirectionalDragAreaPrivate *d;
133−};
134−
135−#endif // DIRECTIONAL_DRAG_AREA_H
plugins/Ubuntu/Gestures/DirectionalDragArea_p.hdeleted−167
@@ -1,167 +0,0 @@
1−/*
2− * Copyright (C) 2015 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_PRIV_H
18−#define DIRECTIONAL_DRAG_AREA_PRIV_H
19−
20−// Information about an active touch point
21−struct UBUNTUGESTURESQML_EXPORT ActiveTouchInfo {
22− ActiveTouchInfo() : id(-1), startTime(-1) {}
23− bool isValid() const { return id != -1; }
24− void reset() { id = -1; }
25− int id;
26− qint64 startTime;
27−};
28−class UBUNTUGESTURESQML_EXPORT ActiveTouchesInfo {
29−public:
30− ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
31− void update(QTouchEvent *event);
32− qint64 touchStartTime(int id);
33− bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
34− qint64 mostRecentStartTime();
35− UbuntuGestures::SharedTimeSource m_timeSource;
36−private:
37− void addTouchPoint(int touchId);
38− void removeTouchPoint(int touchId);
39− #if ACTIVETOUCHESINFO_DEBUG
40− QString toString();
41− #endif
42−
43− Pool<ActiveTouchInfo> m_touchInfoPool;
44−};
45−
46−class UBUNTUGESTURESQML_EXPORT DirectionalDragAreaPrivate : public QObject {
47− Q_OBJECT
48−
49− Q_ENUMS(Status)
50−public:
51− DirectionalDragAreaPrivate(DirectionalDragArea *q);
52−
53−public Q_SLOTS:
54− void giveUpIfDisabledOrInvisible();
55− void rejectGesture();
56−
57−public:
58− // Describes the state of the directional drag gesture.
59− enum Status {
60− // Waiting for a new touch point to land on this area. No gesture is being processed
61− // or tracked.
62− WaitingForTouch,
63−
64− // A touch point has landed on this area but it's not know yet whether it is
65− // performing a drag in the correct direction.
66− // If it's decided that the touch point is not performing a directional drag gesture,
67− // it will be rejected/ignored and status will return to WaitingForTouch.
68− Undecided, //Recognizing,
69−
70− // There's a touch point in this area and it performed a drag in the correct
71− // direction.
72− //
73− // Once recognized, the gesture state will move back to WaitingForTouch only once
74− // that touch point ends. The gesture will remain in the Recognized state even if
75− // the touch point starts moving in other directions or halts.
76− Recognized,
77− };
78−
79− void touchEvent_absent(QTouchEvent *event);
80− void touchEvent_undecided(QTouchEvent *event);
81− void touchEvent_recognized(QTouchEvent *event);
82− bool movingInRightDirection() const;
83− bool movedFarEnoughAlongGestureAxis() const;
84− bool isPastMaxDistance() const;
85− const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
86− void setStatus(Status newStatus);
87− void setPublicPos(const QPointF point);
88− void setPublicScenePos(const QPointF point);
89− bool isWithinTouchCompositionWindow();
90− void updateSceneDirectionVector();
91− // returns the scalar projection between the given vector (in scene coordinates)
92− // and m_sceneDirectionVector
93− qreal projectOntoDirectionVector(const QPointF sceneVector) const;
94− void touchOwnershipEvent(TouchOwnershipEvent *event);
95− void unownedTouchEvent(UnownedTouchEvent *event);
96− void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
97− void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
98− bool recognitionIsDisabled() const;
99− bool sanityCheckRecognitionProperties();
100− void updateSceneDistance();
101− void setMaxTime(int value);
102− void setDistanceThreshold(qreal value);
103− void setPixelsPerMm(qreal pixelsPerMm);
104− QString objectName() const { return q->objectName(); }
105−
106− // Replaces the existing Timer with the given one.
107− //
108− // Useful for providing a fake timer when testing.
109− void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
110−
111− // Useful for testing, where a fake time source can be supplied
112− void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
113−
114− DirectionalDragArea *q;
115−
116− // The current status of the directional drag gesture area.
117− Status status;
118−
119− QPointF startPos;
120− QPointF startScenePos;
121− qreal sceneDistance;
122− int touchId;
123−
124− // The touch position exposed in the public API.
125− // It only starts to move once the gesture gets recognized.
126− QPointF publicPos;
127− QPointF publicScenePos;
128−
129− // A movement damper is used in some of the gesture recognition calculations
130− // to get rid of noise or small oscillations in the touch position.
131− DampedPointF dampedScenePos;
132− QPointF previousDampedScenePos;
133−
134− // Unit vector in scene coordinates describing the direction of the gesture recognition
135− QPointF sceneDirectionVector;
136−
137− Direction::Type direction;
138−
139− // How far a touch point has to move from its initial position along the gesture axis in order
140− // for it to be recognized as a directional drag.
141− qreal distanceThreshold;
142− qreal distanceThresholdSquared; // it's pow(distanceThreshold, 2)
143−
144− // Maximum time (in milliseconds) the gesture can take to go beyond the distance threshold
145− int maxTime;
146−
147− // Maximum distance the gesture can go without crossing the axis-aligned distance threshold
148− qreal maxDistance;
149−
150− // Maximum time (in milliseconds) after the start of a given touch point where
151− // subsequent touch starts are grouped with the first one into an N-touches gesture
152− // (e.g. a two-fingers tap or drag).
153− int compositionTime;
154−
155− bool immediateRecognition;
156−
157− UbuntuGestures::AbstractTimer *recognitionTimer;
158−
159− UbuntuGestures::SharedTimeSource timeSource;
160−
161− ActiveTouchesInfo activeTouches;
162−
163−Q_SIGNALS:
164− void statusChanged(Status value);
165−};
166−
167−#endif // DIRECTIONAL_DRAG_AREA_PRIV_H
plugins/Ubuntu/Gestures/PressedOutsideNotifier.h+1−1
@@ -23,7 +23,7 @@
2323#include <QPointer>
2424#include <QTimer>
2525
26−#include "UbuntuGesturesGlobal.h"
26+#include <ubuntugesturesglobal.h>
2727
2828/*
2929 Notifies when a point, mouse or touch, is pressed outside its area.
plugins/Ubuntu/Gestures/TouchGate.cpp+2−2
@@ -20,8 +20,8 @@
2020#include <QDebug>
2121#include <QQuickWindow>
2222
23−#include <TouchOwnershipEvent.h>
24−#include <TouchRegistry.h>
23+#include <TouchOwnershipEvent>
24+#include <TouchRegistry>
2525
2626#if TOUCHGATE_DEBUG
2727#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
plugins/Ubuntu/Gestures/plugin.cpp+1−3
@@ -17,7 +17,6 @@
1717#include "plugin.h"
1818#include "AxisVelocityCalculator.h"
1919#include "Direction.h"
20−#include "DirectionalDragArea.h"
2120#include "FloatingFlickable.h"
2221#include "PressedOutsideNotifier.h"
2322#include "TouchGate.h"
@@ -33,9 +32,8 @@ static QObject* directionSingleton(QQmlEngine* engine, QJSEngine* scriptEngine)
3332void UbuntuGesturesQmlPlugin::registerTypes(const char *uri)
3433{
3534 qmlRegisterSingletonType<Direction>(uri, 0, 1, "Direction", directionSingleton);
36− qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea");
3735 qmlRegisterType<AxisVelocityCalculator>(uri, 0, 1, "AxisVelocityCalculator");
38− qmlRegisterType<FloatingFlickable>(uri, 0, 1, "FloatingFlickable");
36+// qmlRegisterType<FloatingFlickable>(uri, 0, 1, "FloatingFlickable"); TODO
3937 qmlRegisterType<PressedOutsideNotifier>(uri, 0, 1, "PressedOutsideNotifier");
4038 qmlRegisterType<TouchGate>(uri, 0, 1, "TouchGate");
4139}
qml/Components/DragHandle.qml+2−2
@@ -38,12 +38,12 @@ import Ubuntu.Gestures 0.1
3838 anchors.bottom: parent.bottom
3939 width: units.gu(2)
4040
41− direction: DirectionalDragArea::Leftwards
41+ direction: SwipeArea::Leftwards
4242 }
4343 }
4444
4545 */
46−DirectionalDragArea {
46+SwipeArea {
4747 id: dragArea
4848
4949 property bool stretch: false
qml/Dash/Dash.qml+3−3
@@ -349,7 +349,7 @@ Showable {
349349 }
350350 }
351351
352− DirectionalDragArea {
352+ SwipeArea {
353353 id: overviewDragHandle
354354 objectName: "overviewDragHandle"
355355 z: 1
@@ -364,10 +364,10 @@ Showable {
364364 anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
365365 height: units.gu(2)
366366
367− onSceneDistanceChanged: {
367+ onDistanceChanged: {
368368 if (dragging) {
369369 bottomEdgeController.enableAnimation = false;
370− bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
370+ bottomEdgeController.progress = Math.max(0, Math.min(1, distance / fullMovement));
371371 }
372372 }
373373
qml/Launcher/Launcher.qml+1−1
@@ -275,7 +275,7 @@ Item {
275275 }
276276 }
277277
278− DirectionalDragArea {
278+ SwipeArea {
279279 id: dragArea
280280 objectName: "launcherDragArea"
281281
qml/Panel/IndicatorsMenu.qml+1
@@ -225,6 +225,7 @@ Showable {
225225 stretch: true
226226 maxTotalDragDistance: openedHeight - expandedPanelHeight - handle.height
227227
228+ // TODO touchScene is gone :/
228229 onTouchSceneXChanged: {
229230 if (root.state === "locked") {
230231 d.xDisplacementSinceLock += (touchSceneX - d.lastHideTouchSceneX)
qml/Stages/DesktopStage.qml+1−1
@@ -499,7 +499,7 @@ AbstractStage {
499499 }
500500 }
501501
502− DirectionalDragArea {
502+ SwipeArea {
503503 direction: Direction.Leftwards
504504 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
505505 width: units.gu(1)
qml/Stages/PhoneStage.qml+7−4
@@ -586,7 +586,7 @@ AbstractStage {
586586 enabled: spreadDragArea.dragging
587587 }
588588
589− DirectionalDragArea {
589+ SwipeArea {
590590 id: spreadDragArea
591591 objectName: "spreadDragArea"
592592 direction: Direction.Leftwards
@@ -597,10 +597,11 @@ AbstractStage {
597597
598598 property var gesturePoints: new Array()
599599
600− onTouchXChanged: {
600+ onTouchPositionChanged: {
601+ console.log("onTouchPositionChanged", touchPosition, dragging);
601602 if (dragging) {
602603 // Gesture recognized. Let's move the spreadView with the finger
603− var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
604+ var dragX = Math.min(touchPosition.x + width, width); // Prevent dragging rightwards
604605 dragX = -dragX + spreadDragArea.width - spreadView.shift;
605606 // Don't allow dragging further than the animation crossing with phase2's animation
606607 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
@@ -612,10 +613,11 @@ AbstractStage {
612613 spreadView.contentX = -spreadView.shift;
613614 }
614615
615− gesturePoints.push(touchX);
616+ gesturePoints.push(touchPosition.x);
616617 }
617618
618619 onDraggingChanged: {
620+ console.log("onDraggingChanged", dragging);
619621 if (dragging) {
620622 // A potential edge-drag gesture has started. Start recording it
621623 gesturePoints = [];
@@ -630,6 +632,7 @@ AbstractStage {
630632 }
631633 smallestX = gesturePoints[i];
632634 }
635+ console.log("oneWayFlick", oneWayFlick, "smallestX", smallestX);
633636 gesturePoints = [];
634637
635638 if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
qml/Stages/TabletStage.qml+4−4
@@ -748,7 +748,7 @@ AbstractStage {
748748 enabled: spreadDragArea.dragging
749749 }
750750
751− DirectionalDragArea {
751+ SwipeArea {
752752 id: spreadDragArea
753753 objectName: "spreadDragArea"
754754 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
@@ -758,18 +758,18 @@ AbstractStage {
758758
759759 property var gesturePoints: new Array()
760760
761− onTouchXChanged: {
761+ onTouchPositionChanged: {
762762 if (!dragging) {
763763 spreadView.phase = 0;
764764 spreadView.contentX = -spreadView.shift;
765765 }
766766
767767 if (dragging) {
768− var dragX = -touchX + spreadDragArea.width - spreadView.shift;
768+ var dragX = -touchPosition.x + spreadDragArea.width - spreadView.shift;
769769 var maxDrag = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
770770 spreadView.contentX = Math.min(dragX, maxDrag);
771771 }
772− gesturePoints.push(touchX);
772+ gesturePoints.push(touchPosition.x);
773773 }
774774
775775 onDraggingChanged: {
qml/Tutorial/TutorialBottom.qml+1−1
@@ -73,7 +73,7 @@ TutorialPage {
7373 ]
7474 }
7575
76− DirectionalDragArea {
76+ SwipeArea {
7777 id: dragArea
7878 direction: Direction.Upwards
7979 anchors {
tests/CMakeLists.txt−1
@@ -128,7 +128,6 @@ endfunction()
128128
129129
130130# Actual test definitions
131−add_subdirectory(libs)
132131add_subdirectory(plugins)
133132add_subdirectory(qmltests)
134133add_subdirectory(whitespace)
tests/libs/CMakeLists.txtdeleted−1
@@ -1 +0,0 @@
1−add_subdirectory(UbuntuGestures)
tests/libs/UbuntuGestures/CMakeLists.txtdeleted−16
@@ -1,16 +0,0 @@
1−include_directories(
2− ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
3− ${CMAKE_CURRENT_BINARY_DIR}
4− )
5−
6−macro(add_gesture_test CLASSNAME)
7− add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp)
8− qt5_use_modules(${CLASSNAME}TestExec Test Core Gui Quick)
9− target_link_libraries(${CLASSNAME}TestExec UbuntuGestures)
10−
11− add_unity8_unittest(${CLASSNAME} ${CLASSNAME}TestExec
12− ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/libs/UbuntuGestures
13− )
14−endmacro(add_gesture_test)
15−
16−add_gesture_test(TouchRegistry)
tests/libs/UbuntuGestures/tst_TouchRegistry.cppdeleted−915
@@ -1,915 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 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>
18−#include <QSet>
19−#include <QTouchEvent>
20−
21−#include <Timer.h>
22−#include <TouchOwnershipEvent.h>
23−#include <TouchRegistry.h>
24−#include <UnownedTouchEvent.h>
25−
26−using namespace UbuntuGestures;
27−
28−class TouchMemento {
29−public:
30− TouchMemento(const QTouchEvent *touchEvent);
31− Qt::TouchPointStates touchPointStates;
32− QList<QTouchEvent::TouchPoint> touchPoints;
33−
34− bool containsTouchWithId(int touchId) const;
35−};
36−
37−class DummyCandidate : public QQuickItem
38−{
39− Q_OBJECT
40−public:
41− bool event(QEvent *e) override;
42− QSet<int> ownedTouches;
43− QSet<int> lostTouches;
44− QList<TouchMemento> unownedTouchEvents;
45−};
46−
47−class tst_TouchRegistry : public QObject
48−{
49− Q_OBJECT
50−public:
51− tst_TouchRegistry() : QObject(0) { }
52−private Q_SLOTS:
53− void initTestCase() {} // will be called before the first test function is executed
54− void cleanupTestCase() {} // will be called after the last test function was executed.
55−
56− void init(); // called right before each and every test function is executed
57− void cleanup(); // called right after each and every test function is executed
58−
59− void requestWithNoCandidates();
60− void lateCandidateRequestGetsNothing();
61− void lateCandidateGestOwnershipOnceEarlyCandidateQuits();
62− void dispatchesTouchEventsToCandidates();
63− void dispatchesTouchEventsToWatchers();
64− void keepDispatchingToWatchersAfterLastCandidateGivesUp();
65− void candidatesAndWatchers_1();
66− void candidatesAndWatchers_2();
67− void rejectingTouchfterItsEnd();
68− void removeOldUndecidedCandidates();
69− void interimOwnerWontGetUnownedTouchEvents();
70− void candidateVanishes();
71−
72−private:
73− TouchRegistry *touchRegistry;
74−};
75−
76−void tst_TouchRegistry::init()
77−{
78− touchRegistry = TouchRegistry::instance();
79−}
80−
81−void tst_TouchRegistry::cleanup()
82−{
83− delete touchRegistry;
84−}
85−
86−void tst_TouchRegistry::requestWithNoCandidates()
87−{
88− DummyCandidate candidate;
89−
90− {
91− QList<QTouchEvent::TouchPoint> touchPoints;
92− touchPoints.append(QTouchEvent::TouchPoint(0));
93− touchPoints[0].setState(Qt::TouchPointPressed);
94− QTouchEvent touchEvent(QEvent::TouchBegin,
95− 0 /* device */,
96− Qt::NoModifier,
97− Qt::TouchPointPressed,
98− touchPoints);
99− touchRegistry->update(&touchEvent);
100− }
101−
102− touchRegistry->requestTouchOwnership(0, &candidate);
103−
104− QVERIFY(candidate.ownedTouches.contains(0));
105−}
106−
107−void tst_TouchRegistry::lateCandidateRequestGetsNothing()
108−{
109− DummyCandidate earlyCandidate;
110− earlyCandidate.setObjectName("early");
111− DummyCandidate lateCandidate;
112− lateCandidate.setObjectName("late");
113−
114− {
115− QList<QTouchEvent::TouchPoint> touchPoints;
116− touchPoints.append(QTouchEvent::TouchPoint(0));
117− touchPoints[0].setState(Qt::TouchPointPressed);
118− QTouchEvent touchEvent(QEvent::TouchBegin,
119− 0 /* device */,
120− Qt::NoModifier,
121− Qt::TouchPointPressed,
122− touchPoints);
123− touchRegistry->update(&touchEvent);
124− }
125−
126− touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
127− touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
128−
129− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
130− QVERIFY(lateCandidate.ownedTouches.isEmpty());
131−
132− touchRegistry->requestTouchOwnership(0, &lateCandidate);
133−
134− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
135− QVERIFY(lateCandidate.ownedTouches.isEmpty());
136−
137− touchRegistry->requestTouchOwnership(0, &earlyCandidate);
138−
139− QVERIFY(earlyCandidate.ownedTouches.contains(0));
140− QVERIFY(lateCandidate.ownedTouches.isEmpty());
141−}
142−
143−void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
144−{
145− DummyCandidate earlyCandidate;
146− DummyCandidate lateCandidate;
147−
148− {
149− QList<QTouchEvent::TouchPoint> touchPoints;
150− touchPoints.append(QTouchEvent::TouchPoint(0));
151− touchPoints[0].setState(Qt::TouchPointPressed);
152− QTouchEvent touchEvent(QEvent::TouchBegin,
153− 0 /* device */,
154− Qt::NoModifier,
155− Qt::TouchPointPressed,
156− touchPoints);
157− touchRegistry->update(&touchEvent);
158− }
159−
160− touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
161− touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
162−
163− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
164− QVERIFY(lateCandidate.ownedTouches.isEmpty());
165−
166− touchRegistry->requestTouchOwnership(0, &lateCandidate);
167−
168− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
169− QVERIFY(lateCandidate.ownedTouches.isEmpty());
170−
171− touchRegistry->removeCandidateOwnerForTouch(0, &earlyCandidate);
172−
173− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
174− QVERIFY(lateCandidate.ownedTouches.contains(0));
175−}
176−
177−void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
178−{
179− QQuickItem rootItem;
180−
181− DummyCandidate candidate0;
182− candidate0.setObjectName("0");
183− candidate0.setParentItem(&rootItem);
184− candidate0.setX(1);
185− candidate0.setY(2);
186−
187− DummyCandidate candidate1;
188− candidate1.setObjectName("1");
189− candidate0.setParentItem(&rootItem);
190− candidate1.setX(3);
191− candidate1.setY(4);
192−
193− {
194− QList<QTouchEvent::TouchPoint> touchPoints;
195− touchPoints.append(QTouchEvent::TouchPoint(0));
196− touchPoints[0].setState(Qt::TouchPointPressed);
197− touchPoints[0].setRect(QRect(10, 10, 0, 0));
198− QTouchEvent touchEvent(QEvent::TouchBegin,
199− 0 /* device */,
200− Qt::NoModifier,
201− Qt::TouchPointPressed,
202− touchPoints);
203− touchRegistry->update(&touchEvent);
204− }
205−
206− touchRegistry->addCandidateOwnerForTouch(0, &candidate0);
207−
208− {
209− QList<QTouchEvent::TouchPoint> touchPoints;
210− touchPoints.append(QTouchEvent::TouchPoint(0));
211− touchPoints[0].setState(Qt::TouchPointMoved);
212− touchPoints[0].setRect(QRect(11, 11, 0, 0));
213− touchPoints.append(QTouchEvent::TouchPoint(1));
214− touchPoints[1].setState(Qt::TouchPointPressed);
215− touchPoints[1].setRect(QRect(20, 20, 0, 0));
216− QTouchEvent touchEvent(QEvent::TouchUpdate,
217− 0 /* device */,
218− Qt::NoModifier,
219− Qt::TouchPointPressed | Qt::TouchPointMoved,
220− touchPoints);
221− touchRegistry->update(&touchEvent);
222− }
223−
224− // candidate0 should have received an update on the touch he's interested on.
225− QCOMPARE(candidate0.unownedTouchEvents.count(), 1);
226− // make sure only touch 0 is there (i.e. no mention of touch 1)
227− QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints.count(), 1);
228− QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].id(), 0);
229− // Check that the points local coordinates have been mapped to candidate0's coordinate system.
230− QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - candidate0.x());
231− QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - candidate0.y());
232−
233− touchRegistry->addCandidateOwnerForTouch(1, &candidate1);
234−
235− {
236− QList<QTouchEvent::TouchPoint> touchPoints;
237− touchPoints.append(QTouchEvent::TouchPoint(0));
238− touchPoints[0].setState(Qt::TouchPointMoved);
239− touchPoints[0].setRect(QRect(12, 12, 0, 0));
240− touchPoints.append(QTouchEvent::TouchPoint(1));
241− touchPoints[1].setState(Qt::TouchPointMoved);
242− touchPoints[1].setRect(QRect(21, 21, 0, 0));
243− QTouchEvent touchEvent(QEvent::TouchUpdate,
244− 0 /* device */,
245− Qt::NoModifier,
246− Qt::TouchPointPressed | Qt::TouchPointMoved,
247− touchPoints);
248− touchRegistry->update(&touchEvent);
249− }
250−
251− // candidate0 gets updates only for touch 0 and
252− // candidate1 gets updates only for touch 1
253−
254− QCOMPARE(candidate0.unownedTouchEvents.count(), 2);
255− QCOMPARE(candidate0.unownedTouchEvents[1].touchPoints.count(), 1);
256− QCOMPARE(candidate0.unownedTouchEvents[1].touchPoints[0].id(), 0);
257−
258− QCOMPARE(candidate1.unownedTouchEvents.count(), 1);
259− QCOMPARE(candidate1.unownedTouchEvents[0].touchPoints.count(), 1);
260− QCOMPARE(candidate1.unownedTouchEvents[0].touchPoints[0].id(), 1);
261−}
262−
263−void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
264−{
265− QQuickItem rootItem;
266−
267− DummyCandidate watcher;
268− watcher.setObjectName("watcher");
269− watcher.setParentItem(&rootItem);
270− watcher.setX(1);
271− watcher.setY(2);
272−
273− {
274− QList<QTouchEvent::TouchPoint> touchPoints;
275− touchPoints.append(QTouchEvent::TouchPoint(0));
276− touchPoints[0].setState(Qt::TouchPointPressed);
277− touchPoints[0].setRect(QRect(10, 10, 0, 0));
278− QTouchEvent touchEvent(QEvent::TouchBegin,
279− 0 /* device */,
280− Qt::NoModifier,
281− Qt::TouchPointPressed,
282− touchPoints);
283− touchRegistry->update(&touchEvent);
284− }
285−
286− touchRegistry->addTouchWatcher(0, &watcher);
287−
288− {
289− QList<QTouchEvent::TouchPoint> touchPoints;
290− touchPoints.append(QTouchEvent::TouchPoint(0));
291− touchPoints[0].setState(Qt::TouchPointMoved);
292− touchPoints[0].setRect(QRect(11, 11, 0, 0));
293− touchPoints.append(QTouchEvent::TouchPoint(1));
294− touchPoints[1].setState(Qt::TouchPointPressed);
295− touchPoints[1].setRect(QRect(20, 20, 0, 0));
296− QTouchEvent touchEvent(QEvent::TouchUpdate,
297− 0 /* device */,
298− Qt::NoModifier,
299− Qt::TouchPointPressed | Qt::TouchPointMoved,
300− touchPoints);
301− touchRegistry->update(&touchEvent);
302− }
303−
304− // watcher should have received an update on the touch he's interested on.
305− QCOMPARE(watcher.unownedTouchEvents.count(), 1);
306− // make sure only touch 0 is there (i.e. no mention of touch 1)
307− QCOMPARE(watcher.unownedTouchEvents[0].touchPoints.count(), 1);
308− QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].id(), 0);
309− // Check that the points local coordinates have been mapped to watcher's coordinate system.
310− QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - watcher.x());
311− QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - watcher.y());
312−
313− {
314− QList<QTouchEvent::TouchPoint> touchPoints;
315− touchPoints.append(QTouchEvent::TouchPoint(0));
316− touchPoints[0].setState(Qt::TouchPointMoved);
317− touchPoints[0].setRect(QRect(12, 12, 0, 0));
318− touchPoints.append(QTouchEvent::TouchPoint(1));
319− touchPoints[1].setState(Qt::TouchPointMoved);
320− touchPoints[1].setRect(QRect(21, 21, 0, 0));
321− QTouchEvent touchEvent(QEvent::TouchUpdate,
322− 0 /* device */,
323− Qt::NoModifier,
324− Qt::TouchPointPressed | Qt::TouchPointMoved,
325− touchPoints);
326− touchRegistry->update(&touchEvent);
327− }
328−
329− // watcher gets updates only for touch 0
330−
331− QCOMPARE(watcher.unownedTouchEvents.count(), 2);
332− QCOMPARE(watcher.unownedTouchEvents[1].touchPoints.count(), 1);
333− QCOMPARE(watcher.unownedTouchEvents[1].touchPoints[0].id(), 0);
334−}
335−
336−void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
337−{
338− DummyCandidate item;
339−
340− {
341− QList<QTouchEvent::TouchPoint> touchPoints;
342− touchPoints.append(QTouchEvent::TouchPoint(0));
343− touchPoints[0].setState(Qt::TouchPointPressed);
344− QTouchEvent touchEvent(QEvent::TouchBegin,
345− 0 /* device */,
346− Qt::NoModifier,
347− Qt::TouchPointPressed,
348− touchPoints);
349− touchRegistry->update(&touchEvent);
350− }
351−
352− touchRegistry->addCandidateOwnerForTouch(0, &item);
353−
354− {
355− QList<QTouchEvent::TouchPoint> touchPoints;
356− touchPoints.append(QTouchEvent::TouchPoint(0));
357− touchPoints[0].setState(Qt::TouchPointMoved);
358− touchPoints.append(QTouchEvent::TouchPoint(1));
359− touchPoints[1].setState(Qt::TouchPointPressed);
360− QTouchEvent touchEvent(QEvent::TouchUpdate,
361− 0 /* device */,
362− Qt::NoModifier,
363− Qt::TouchPointPressed | Qt::TouchPointMoved,
364− touchPoints);
365− touchRegistry->update(&touchEvent);
366− }
367−
368− QCOMPARE(item.unownedTouchEvents.count(), 1);
369− QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 1);
370− QCOMPARE(item.unownedTouchEvents[0].touchPoints[0].id(), 0);
371− item.unownedTouchEvents.clear();
372−
373− touchRegistry->addTouchWatcher(1, &item);
374−
375− {
376− QList<QTouchEvent::TouchPoint> touchPoints;
377− touchPoints.append(QTouchEvent::TouchPoint(0));
378− touchPoints[0].setState(Qt::TouchPointMoved);
379− touchPoints.append(QTouchEvent::TouchPoint(1));
380− touchPoints[1].setState(Qt::TouchPointMoved);
381− QTouchEvent touchEvent(QEvent::TouchUpdate,
382− 0 /* device */,
383− Qt::NoModifier,
384− Qt::TouchPointMoved,
385− touchPoints);
386− touchRegistry->update(&touchEvent);
387− }
388−
389− QCOMPARE(item.unownedTouchEvents.count(), 1);
390− QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 2);
391− QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(0));
392− QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
393− item.unownedTouchEvents.clear();
394−
395− {
396− QList<QTouchEvent::TouchPoint> touchPoints;
397− touchPoints.append(QTouchEvent::TouchPoint(0));
398− touchPoints[0].setState(Qt::TouchPointReleased);
399− touchPoints.append(QTouchEvent::TouchPoint(1));
400− touchPoints[1].setState(Qt::TouchPointMoved);
401− QTouchEvent touchEvent(QEvent::TouchUpdate,
402− 0 /* device */,
403− Qt::NoModifier,
404− Qt::TouchPointReleased | Qt::TouchPointMoved,
405− touchPoints);
406− touchRegistry->update(&touchEvent);
407− }
408−
409− QCOMPARE(item.unownedTouchEvents.count(), 1);
410− QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 2);
411− QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(0));
412− QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
413− item.unownedTouchEvents.clear();
414−
415− touchRegistry->removeCandidateOwnerForTouch(0, &item);
416−
417− {
418− QList<QTouchEvent::TouchPoint> touchPoints;
419− touchPoints.append(QTouchEvent::TouchPoint(1));
420− touchPoints[0].setState(Qt::TouchPointReleased);
421− QTouchEvent touchEvent(QEvent::TouchEnd,
422− 0 /* device */,
423− Qt::NoModifier,
424− Qt::TouchPointReleased,
425− touchPoints);
426− touchRegistry->update(&touchEvent);
427− }
428−
429− QCOMPARE(item.unownedTouchEvents.count(), 1);
430− QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 1);
431− QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
432− item.unownedTouchEvents.clear();
433−
434− QVERIFY(touchRegistry->m_touchInfoPool.isEmpty());
435−}
436−
437−/*
438− Regression test that reproduces a problematic scenario that came up during manual testing.
439− It reproduces the interaction between TouchRegistry and a DirectionalDragArea
440− */
441−void tst_TouchRegistry::candidatesAndWatchers_1()
442−{
443− DummyCandidate item;
444−
445− {
446− QList<QTouchEvent::TouchPoint> touchPoints;
447− touchPoints.append(QTouchEvent::TouchPoint(0));
448− touchPoints[0].setState(Qt::TouchPointPressed);
449− QTouchEvent touchEvent(QEvent::TouchBegin,
450− 0 /* device */,
451− Qt::NoModifier,
452− Qt::TouchPointPressed,
453− touchPoints);
454− touchRegistry->update(&touchEvent);
455− }
456−
457− touchRegistry->addCandidateOwnerForTouch(0, &item);
458−
459− {
460− QList<QTouchEvent::TouchPoint> touchPoints;
461− touchPoints.append(QTouchEvent::TouchPoint(0));
462− touchPoints[0].setState(Qt::TouchPointMoved);
463− touchPoints.append(QTouchEvent::TouchPoint(1));
464− touchPoints[1].setState(Qt::TouchPointPressed);
465− QTouchEvent touchEvent(QEvent::TouchUpdate,
466− 0 /* device */,
467− Qt::NoModifier,
468− Qt::TouchPointPressed | Qt::TouchPointMoved,
469− touchPoints);
470− touchRegistry->update(&touchEvent);
471− }
472−
473− touchRegistry->addTouchWatcher(1, &item);
474−
475− touchRegistry->removeCandidateOwnerForTouch(0, &item);
476−
477− touchRegistry->addTouchWatcher(0, &item);
478−
479− {
480− QList<QTouchEvent::TouchPoint> touchPoints;
481− touchPoints.append(QTouchEvent::TouchPoint(0));
482− touchPoints[0].setState(Qt::TouchPointReleased);
483− touchPoints.append(QTouchEvent::TouchPoint(1));
484− touchPoints[1].setState(Qt::TouchPointMoved);
485− QTouchEvent touchEvent(QEvent::TouchUpdate,
486− 0 /* device */,
487− Qt::NoModifier,
488− Qt::TouchPointReleased | Qt::TouchPointMoved,
489− touchPoints);
490− touchRegistry->update(&touchEvent);
491− }
492−
493− {
494− QList<QTouchEvent::TouchPoint> touchPoints;
495− touchPoints.append(QTouchEvent::TouchPoint(1));
496− touchPoints[0].setState(Qt::TouchPointReleased);
497− QTouchEvent touchEvent(QEvent::TouchEnd,
498− 0 /* device */,
499− Qt::NoModifier,
500− Qt::TouchPointReleased,
501− touchPoints);
502− touchRegistry->update(&touchEvent);
503− }
504−
505− QVERIFY(touchRegistry->m_touchInfoPool.isEmpty());
506−
507− item.unownedTouchEvents.clear();
508−
509− {
510− QList<QTouchEvent::TouchPoint> touchPoints;
511− touchPoints.append(QTouchEvent::TouchPoint(0));
512− touchPoints[0].setState(Qt::TouchPointPressed);
513− QTouchEvent touchEvent(QEvent::TouchBegin,
514− 0 /* device */,
515− Qt::NoModifier,
516− Qt::TouchPointPressed,
517− touchPoints);
518− touchRegistry->update(&touchEvent);
519− }
520−
521− // Haven't made any subscription for that new touch 0.
522− QCOMPARE(item.unownedTouchEvents.count(), 0);
523−}
524−
525−/*
526− Regression test that reproduces a problematic scenario that came up during manual testing.
527− It reproduces the interaction between TouchRegistry, DirectionalDragArea and a TouchGate.
528− */
529−void tst_TouchRegistry::candidatesAndWatchers_2()
530−{
531− DummyCandidate directionalDragArea;
532− directionalDragArea.setObjectName("DirectionalDragArea");
533−
534− DummyCandidate touchGate;
535− touchGate.setObjectName("TouchGate");
536−
537− // [DDA] 1298 TouchBegin (id:0, state:pressed, scenePos:(147,1023))
538− {
539− QList<QTouchEvent::TouchPoint> touchPoints;
540− touchPoints.append(QTouchEvent::TouchPoint(0));
541− touchPoints[0].setState(Qt::TouchPointPressed);
542− QTouchEvent touchEvent(QEvent::TouchBegin,
543− 0 /* device */,
544− Qt::NoModifier,
545− Qt::TouchPointPressed,
546− touchPoints);
547− touchRegistry->update(&touchEvent);
548− }
549−
550− // [TouchRegistry] addCandidateOwnerForTouch id 0 candidate DirectionalDragArea
551− touchRegistry->addCandidateOwnerForTouch(0, &directionalDragArea);
552−
553− // [TouchRegistry] requestTouchOwnership id 0 candidate TouchGate
554− touchRegistry->requestTouchOwnership(0, &touchGate);
555−
556− //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(147,1023)) (id:1, state:pressed, scenePos:(327,792))
557− {
558− QList<QTouchEvent::TouchPoint> touchPoints;
559− touchPoints.append(QTouchEvent::TouchPoint(0));
560− touchPoints[0].setState(Qt::TouchPointMoved);
561− touchPoints.append(QTouchEvent::TouchPoint(1));
562− touchPoints[1].setState(Qt::TouchPointPressed);
563− QTouchEvent touchEvent(QEvent::TouchUpdate,
564− 0 /* device */,
565− Qt::NoModifier,
566− Qt::TouchPointPressed | Qt::TouchPointMoved,
567− touchPoints);
568− touchRegistry->update(&touchEvent);
569− }
570−
571− // [TouchRegistry] addTouchWatcher id 1 watcher DirectionalDragArea
572− touchRegistry->addTouchWatcher(1, &directionalDragArea);
573−
574− // [TouchRegistry] removeCandidateOwnerForTouch id 0 candidate DirectionalDragArea
575− touchRegistry->removeCandidateOwnerForTouch(0, &directionalDragArea);
576−
577− //[TouchRegistry] sending TouchWonershipEvent(id = 0 gained) to candidate TouchGate
578− QCOMPARE(touchGate.ownedTouches.count(), 1);
579− QVERIFY(touchGate.ownedTouches.contains(0));
580−
581− // [TouchRegistry] addTouchWatcher id 0 watcher DirectionalDragArea
582− touchRegistry->addTouchWatcher(0, &directionalDragArea);
583−
584− // [TouchRegistry] requestTouchOwnership id 1 candidate TouchGate
585− touchRegistry->requestTouchOwnership(1, &touchGate);
586−
587− //[TouchRegistry] sending TouchWonershipEvent(id = 1 gained) to candidate TouchGate
588− QCOMPARE(touchGate.ownedTouches.count(), 2);
589− QVERIFY(touchGate.ownedTouches.contains(1));
590−
591− directionalDragArea.unownedTouchEvents.clear();
592− touchGate.unownedTouchEvents.clear();
593−
594− //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(148,1025)) (id:1, state:moved, scenePos:(329,795))
595− {
596− QList<QTouchEvent::TouchPoint> touchPoints;
597− touchPoints.append(QTouchEvent::TouchPoint(0));
598− touchPoints[0].setState(Qt::TouchPointMoved);
599− touchPoints.append(QTouchEvent::TouchPoint(1));
600− touchPoints[1].setState(Qt::TouchPointMoved);
601− QTouchEvent touchEvent(QEvent::TouchUpdate,
602− 0 /* device */,
603− Qt::NoModifier,
604− Qt::TouchPointMoved,
605− touchPoints);
606− touchRegistry->update(&touchEvent);
607− }
608−
609− //vvvvv DDA Watchers are being ignored from now on vvvvvvv
610−
611− QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
612− QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 2);
613− QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0));
614− QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(1));
615−
616− QVERIFY(touchGate.unownedTouchEvents.isEmpty());
617−
618− directionalDragArea.unownedTouchEvents.clear();
619−
620− //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(151,1029)) (id:1, state:released, scenePos:(329,795))
621− {
622− QList<QTouchEvent::TouchPoint> touchPoints;
623− touchPoints.append(QTouchEvent::TouchPoint(0));
624− touchPoints[0].setState(Qt::TouchPointMoved);
625− touchPoints.append(QTouchEvent::TouchPoint(1));
626− touchPoints[1].setState(Qt::TouchPointReleased);
627− QTouchEvent touchEvent(QEvent::TouchUpdate,
628− 0 /* device */,
629− Qt::NoModifier,
630− Qt::TouchPointMoved | Qt::TouchPointReleased,
631− touchPoints);
632− touchRegistry->update(&touchEvent);
633− }
634−
635− QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
636− QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 2);
637− QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0));
638− QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(1));
639−
640− QVERIFY(touchGate.unownedTouchEvents.isEmpty());
641−
642− directionalDragArea.unownedTouchEvents.clear();
643−
644− //[TouchRegistry] got TouchEnd (id:0, state:released, scenePos:(157,1034))
645− {
646− QList<QTouchEvent::TouchPoint> touchPoints;
647− touchPoints.append(QTouchEvent::TouchPoint(0));
648− touchPoints[0].setState(Qt::TouchPointReleased);
649− QTouchEvent touchEvent(QEvent::TouchEnd,
650− 0 /* device */,
651− Qt::NoModifier,
652− Qt::TouchPointReleased,
653− touchPoints);
654− touchRegistry->update(&touchEvent);
655− }
656−
657− QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
658− QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 1);
659− QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0));
660−
661− QVERIFY(touchGate.unownedTouchEvents.isEmpty());
662−}
663−
664−void tst_TouchRegistry::rejectingTouchfterItsEnd()
665−{
666− DummyCandidate earlyCandidate;
667− DummyCandidate lateCandidate;
668−
669− {
670− QList<QTouchEvent::TouchPoint> touchPoints;
671− touchPoints.append(QTouchEvent::TouchPoint(0));
672− touchPoints[0].setState(Qt::TouchPointPressed);
673− QTouchEvent touchEvent(QEvent::TouchBegin,
674− 0 /* device */,
675− Qt::NoModifier,
676− Qt::TouchPointPressed,
677− touchPoints);
678− touchRegistry->update(&touchEvent);
679− }
680−
681− touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
682− touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
683−
684− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
685− QVERIFY(lateCandidate.ownedTouches.isEmpty());
686−
687− touchRegistry->requestTouchOwnership(0, &lateCandidate);
688−
689− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
690− QVERIFY(lateCandidate.ownedTouches.isEmpty());
691−
692− {
693− QList<QTouchEvent::TouchPoint> touchPoints;
694− touchPoints.append(QTouchEvent::TouchPoint(0));
695− touchPoints[0].setState(Qt::TouchPointReleased);
696− QTouchEvent touchEvent(QEvent::TouchEnd,
697− 0 /* device */,
698− Qt::NoModifier,
699− Qt::TouchPointPressed,
700− touchPoints);
701− touchRegistry->update(&touchEvent);
702− }
703−
704− QVERIFY(earlyCandidate.ownedTouches.isEmpty());
705− QVERIFY(lateCandidate.ownedTouches.isEmpty());
706−
707− touchRegistry->removeCandidateOwnerForTouch(0, &earlyCandidate);
708−
709− QCOMPARE(lateCandidate.ownedTouches.count(), 1);
710− QCOMPARE(lateCandidate.ownedTouches.contains(0), 1);
711−
712− // Check that there's no trace left of touch 0 as we no longer need to keep tabs on it.
713− QVERIFY(!touchRegistry->findTouchInfo(0));
714−}
715−
716−void tst_TouchRegistry::removeOldUndecidedCandidates()
717−{
718− FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
719− touchRegistry->setTimerFactory(fakeTimerFactory);
720−
721− DummyCandidate undecidedCandidate;
722− undecidedCandidate.setObjectName("undecided");
723−
724− DummyCandidate candidateThatWantsTouch;
725− candidateThatWantsTouch.setObjectName("wantsTouch");
726−
727− {
728− QList<QTouchEvent::TouchPoint> touchPoints;
729− touchPoints.append(QTouchEvent::TouchPoint(0));
730− touchPoints[0].setState(Qt::TouchPointPressed);
731− QTouchEvent touchEvent(QEvent::TouchBegin,
732− 0 /* device */,
733− Qt::NoModifier,
734− Qt::TouchPointPressed,
735− touchPoints);
736− touchRegistry->update(&touchEvent);
737− }
738−
739− touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
740−
741− touchRegistry->requestTouchOwnership(0, &candidateThatWantsTouch);
742−
743− QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
744− QVERIFY(undecidedCandidate.lostTouches.isEmpty());
745− QVERIFY(candidateThatWantsTouch.ownedTouches.isEmpty());
746− QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty());
747−
748− // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
749− // making TouchRegistry consider that undecidedCantidate defaulted.
750− fakeTimerFactory->updateTime(10000);
751−
752− QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
753− QVERIFY(undecidedCandidate.lostTouches.contains(0));
754− QVERIFY(candidateThatWantsTouch.ownedTouches.contains(0));
755− QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty());
756−}
757−
758−/*
759− An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
760− is assumed to be the interim owner of the touch point, thus there's no point in sending
761− UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
762− he didn't ignore the first QTouchEvent with that touch point.
763− */
764−void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
765−{
766− FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
767− touchRegistry->setTimerFactory(fakeTimerFactory);
768−
769− DummyCandidate undecidedCandidate;
770− undecidedCandidate.setObjectName("undecided");
771−
772− DummyCandidate interimOwner;
773− interimOwner.setObjectName("interimOwner");
774−
775− {
776− QList<QTouchEvent::TouchPoint> touchPoints;
777− touchPoints.append(QTouchEvent::TouchPoint(0));
778− touchPoints[0].setState(Qt::TouchPointPressed);
779− QTouchEvent touchEvent(QEvent::TouchBegin,
780− 0 /* device */,
781− Qt::NoModifier,
782− Qt::TouchPointPressed,
783− touchPoints);
784− touchRegistry->update(&touchEvent);
785− }
786−
787− touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
788− touchRegistry->requestTouchOwnership(0, &interimOwner);
789−
790− {
791− QList<QTouchEvent::TouchPoint> touchPoints;
792− touchPoints.append(QTouchEvent::TouchPoint(0));
793− touchPoints[0].setState(Qt::TouchPointMoved);
794− QTouchEvent touchEvent(QEvent::TouchUpdate,
795− 0 /* device */,
796− Qt::NoModifier,
797− Qt::TouchPointMoved,
798− touchPoints);
799− touchRegistry->update(&touchEvent);
800− }
801−
802− QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
803− QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
804−
805− {
806− QList<QTouchEvent::TouchPoint> touchPoints;
807− touchPoints.append(QTouchEvent::TouchPoint(0));
808− touchPoints[0].setState(Qt::TouchPointMoved);
809− QTouchEvent touchEvent(QEvent::TouchUpdate,
810− 0 /* device */,
811− Qt::NoModifier,
812− Qt::TouchPointMoved,
813− touchPoints);
814− touchRegistry->update(&touchEvent);
815− }
816−
817− QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
818− QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
819−}
820−
821−/*
822− Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473492
823−
824− Bug caused by candidates getting deleted before ownership resolution
825− */
826−void tst_TouchRegistry::candidateVanishes()
827−{
828− DummyCandidate mainCandidate;
829− DummyCandidate *missingCandidate = new DummyCandidate;
830−
831− {
832− QList<QTouchEvent::TouchPoint> touchPoints;
833− touchPoints.append(QTouchEvent::TouchPoint(0));
834− touchPoints[0].setState(Qt::TouchPointPressed);
835− QTouchEvent touchEvent(QEvent::TouchBegin,
836− 0 /* device */,
837− Qt::NoModifier,
838− Qt::TouchPointPressed,
839− touchPoints);
840− touchRegistry->update(&touchEvent);
841− }
842−
843− touchRegistry->addCandidateOwnerForTouch(0, &mainCandidate);
844− touchRegistry->addCandidateOwnerForTouch(0, missingCandidate);
845−
846− {
847− QList<QTouchEvent::TouchPoint> touchPoints;
848− touchPoints.append(QTouchEvent::TouchPoint(0));
849− touchPoints[0].setState(Qt::TouchPointMoved);
850− QTouchEvent touchEvent(QEvent::TouchUpdate,
851− 0 /* device */,
852− Qt::NoModifier,
853− Qt::TouchPointMoved,
854− touchPoints);
855− touchRegistry->update(&touchEvent);
856− }
857−
858− // This candidate suddenly disappears without having removed its candidacy.
859− delete missingCandidate;
860− missingCandidate = nullptr;
861−
862− touchRegistry->requestTouchOwnership(0, &mainCandidate);
863−
864− QVERIFY(mainCandidate.ownedTouches.contains(0));
865−}
866−
867−////////////// TouchMemento //////////
868−
869−TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
870− : touchPointStates(touchEvent->touchPointStates()), touchPoints(touchEvent->touchPoints())
871−{
872−}
873−
874−bool TouchMemento::containsTouchWithId(int touchId) const
875−{
876− for (int i = 0; i < touchPoints.count(); ++i) {
877− if (touchPoints.at(i).id() == touchId) {
878− return true;
879− }
880− }
881− return false;
882−}
883−
884−////////////// DummyCandidate //////////
885−
886−bool DummyCandidate::event(QEvent *e)
887−{
888− if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
889− TouchOwnershipEvent *touchOwnershipEvent = static_cast<TouchOwnershipEvent *>(e);
890−
891− // NB: Cannot use QVERIFY here because the macro doesn't return a boolean and is
892− // meant for use only directly in the body of a test function
893− if (ownedTouches.contains(touchOwnershipEvent->touchId()))
894− qFatal("Sent ownership event for a touch that is already owned.");
895− if (lostTouches.contains(touchOwnershipEvent->touchId()))
896− qFatal("Sent ownership event for a touch that has already been lost.");
897−
898− if (touchOwnershipEvent->gained()) {
899− ownedTouches.insert(touchOwnershipEvent->touchId());
900− } else {
901− lostTouches.insert(touchOwnershipEvent->touchId());
902− }
903− return true;
904− } else if (e->type() == UnownedTouchEvent::unownedTouchEventType()) {
905− UnownedTouchEvent *unownedTouchEvent = static_cast<UnownedTouchEvent *>(e);
906− unownedTouchEvents.append(TouchMemento(unownedTouchEvent->touchEvent()));
907− return true;
908− } else {
909− return QObject::event(e);
910− }
911−}
912−
913−QTEST_GUILESS_MAIN(tst_TouchRegistry)
914−
915−#include "tst_TouchRegistry.moc"
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+5−5
@@ -1,5 +1,7 @@
11include(QmlTest)
22
3+pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
4+
35# DANGER! DANGER! Using Qt's private API!
46include_directories(
57 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
@@ -7,7 +9,7 @@ include_directories(
79
810include_directories(
911 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
10− ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
12+ ${UBUNTUGESTURES_INCLUDE_DIRS}
1113 ${CMAKE_CURRENT_BINARY_DIR}
1214 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1315 )
@@ -17,7 +19,7 @@ export_qmlfiles(UbuntuGesturesTest .)
1719macro(build_gesture_test CLASSNAME)
1820 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp TestItem.cpp)
1921 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
20− target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
22+ target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml ${UBUNTUGESTURES_LIBRARIES})
2123endmacro()
2224
2325macro(add_gesture_unit_test CLASSNAME)
@@ -39,10 +41,8 @@ macro(add_gesture_ui_test CLASSNAME)
3941 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${UNITY_PLUGINPATH})
4042endmacro()
4143
42−add_gesture_ui_test(DirectionalDragArea)
43−add_gesture_ui_test(FloatingFlickable)
44+# TODO add_gesture_ui_test(FloatingFlickable)
4445add_gesture_ui_test(PressedOutsideNotifier)
4546add_gesture_ui_test(TouchDispatcher)
4647add_gesture_ui_test(TouchGate)
47−add_gesture_unit_test(Damper)
4848add_gesture_unit_test(AxisVelocityCalculator)
tests/plugins/Ubuntu/Gestures/GestureTest.cpp+2−2
@@ -21,8 +21,8 @@
2121#include <QQuickView>
2222#include <QtTest>
2323
24−#include <Timer.h>
25−#include <TouchRegistry.h>
24+#include <Timer>
25+#include <TouchRegistry>
2626
2727using namespace UbuntuGestures;
2828
tests/plugins/Ubuntu/Gestures/tst_Damper.cppdeleted−40
@@ -1,40 +0,0 @@
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−
21−class tst_Damper : public QObject
22−{
23− Q_OBJECT
24−private Q_SLOTS:
25− void negativeMovement();
26−};
27−
28−void 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−
38−QTEST_MAIN(tst_Damper)
39−
40−#include "tst_Damper.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cppdeleted−1306
@@ -1,1306 +0,0 @@
1−/*
2− * Copyright (C) 2013-2015 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 <QtQuick/QQuickView>
20−#include <QtQml/QQmlEngine>
21−#include <QPointer>
22−#include <private/qquickmousearea_p.h>
23−#include <private/qquickwindow_p.h>
24−
25−
26−#include <DirectionalDragArea.h>
27−#include <DirectionalDragArea_p.h>
28−#include <TouchRegistry.h>
29−
30−#include "GestureTest.h"
31−
32−using namespace UbuntuGestures;
33−
34−// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
35−// doesn't work
36−class StatusSpy : public QObject {
37− Q_OBJECT
38−public:
39− StatusSpy(DirectionalDragArea *edgeDragArea) {
40− m_recognized = false;
41− connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
42− this, &StatusSpy::onStatusChanged);
43− }
44− bool recognized() {
45− return m_recognized;
46− }
47−
48−private Q_SLOTS:
49− void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
50− m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
51− }
52−
53−private:
54− bool m_recognized;
55−};
56−
57−/*
58− QQuickMouseArea::canceled() signal is not registered in the meta object system.
59− So using a QSignalSpy to track it won't work. Thus the only way to connect to it
60− is using its method address directly.
61− */
62−class MouseAreaSpy : public QObject
63−{
64− Q_OBJECT
65−public:
66− MouseAreaSpy(QQuickMouseArea *mouseArea)
67− : canceledCount(0)
68− {
69− connect(mouseArea, &QQuickMouseArea::canceled,
70− this, &MouseAreaSpy::onMouseAreaCanceled);
71− }
72−
73− int canceledCount;
74−
75−private Q_SLOTS:
76− void onMouseAreaCanceled() {
77− ++canceledCount;
78− }
79−};
80−
81−class tst_DirectionalDragArea: public GestureTest
82−{
83− Q_OBJECT
84−public:
85− tst_DirectionalDragArea();
86−private Q_SLOTS:
87− void init() override; // called right before each and every test function is executed
88−
89− void dragWithShortDirectionChange();
90− void recognitionTimerUsage();
91− void sceneXAndX();
92− void sceneYAndY();
93− void twoFingerTap();
94− void movingDDA();
95− void ignoreOldFinger();
96− void rotated();
97− void sceneDistance();
98− void sceneDistance_data();
99− void disabledWhileDragging();
100− void oneFingerDownFollowedByLateSecondFingerDown();
101− void givesUpWhenLosesTouch();
102− void threeFingerDrag();
103− void immediateRecognitionWhenConstraintsDisabled();
104− void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
105− void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
106− void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
107− void interleavedTouches();
108− void makoRightEdgeDrag();
109− void makoRightEdgeDrag_verticalDownwards();
110− void makoLeftEdgeDrag_slowStart();
111− void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
112−
113−private:
114− // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
115− // lose precision due to rounding.
116− // Besides, those helper functions lead to more compact code.
117− void sendTouchPress(qint64 timestamp, int id, QPointF pos);
118− void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
119− void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
120− void sendTouch(qint64 timestamp, int id, QPointF pos,
121− Qt::TouchPointState pointState, QEvent::Type eventType);
122−
123− void passTime(qint64 timeSpanMs);
124−};
125−
126−tst_DirectionalDragArea::tst_DirectionalDragArea()
127− : GestureTest(QStringLiteral("tst_DirectionalDragArea.qml"))
128−{
129−}
130−
131−void tst_DirectionalDragArea::init()
132−{
133− GestureTest::init();
134−
135− // We shouldn't need the three lines below, but a compiz/unity7
136− // regression means we don't pass the test without them because
137− // the window doesn't have the proper size. Can be removed in the
138− // future if the regression is fixed and tests pass again
139− m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
140− QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
141− QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
142−}
143−
144−void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
145−{
146− sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
147−}
148−
149−void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
150−{
151− sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
152−}
153−
154−void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
155−{
156− sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
157−}
158−
159−void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
160− Qt::TouchPointState pointState, QEvent::Type eventType)
161−{
162− m_fakeTimerFactory->updateTime(timestamp);
163−
164− QTouchEvent::TouchPoint point;
165−
166− point.setState(pointState);
167− point.setId(id);
168− point.setScenePos(pos);
169− point.setPos(pos);
170−
171− QList<QTouchEvent::TouchPoint> points;
172− points << point;
173−
174− QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
175− QCoreApplication::sendEvent(m_view, &touchEvent);
176−
177− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
178− windowPrivate->flushDelayedTouchEvent();
179−}
180−
181−void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
182−{
183− qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
184− m_fakeTimerFactory->updateTime(finalTime);
185−}
186−
187−namespace {
188−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
189−{
190− QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
191− return edgeDragArea->mapToScene(localCenter);
192−}
193−}
194−
195−/*
196− A directional drag should still be recognized if there is a momentaneous, small,
197− change in the direction of a drag. That should be accounted as input noise and
198− therefore ignored.
199− */
200−void tst_DirectionalDragArea::dragWithShortDirectionChange()
201−{
202− DirectionalDragArea *edgeDragArea =
203− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
204− QVERIFY(edgeDragArea != 0);
205− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
206− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
207−
208− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
209− QPointF touchPoint = initialTouchPos;
210−
211− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
212− QPointF dragDirectionVector(1.0, 0.0);
213− qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
214− // make sure we are above maximum time
215− int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
216− QPointF touchMovement = dragDirectionVector * touchStepDistance;
217−
218− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
219−
220− // Move a bit in the proper direction
221− for (int i=0; i < 3; ++i) {
222− touchPoint += touchMovement;
223− passTime(touchStepTimeMs);
224− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
225− }
226−
227− // Then a sudden and small movement to the opposite direction
228− touchPoint -= touchMovement*0.2;
229− passTime(touchStepTimeMs*0.2);
230− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
231−
232− // And then resume movment in the correct direction until it crosses the distance and time
233− // thresholds.
234− do {
235− touchPoint += touchMovement;
236− passTime(touchStepTimeMs);
237− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
238− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
239− || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
240−
241− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
242−
243− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
244−}
245−
246−/*
247− Checks that the recognition timer is started and stopped appropriately.
248− I.e., check that it's running only while gesture recognition is taking place
249− (status == Undecided)
250− */
251−void tst_DirectionalDragArea::recognitionTimerUsage()
252−{
253− DirectionalDragArea *edgeDragArea =
254− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
255− QVERIFY(edgeDragArea != 0);
256− AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
257− edgeDragArea->d->setRecognitionTimer(fakeTimer);
258− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
259−
260− int timeStepMs = 5; // some arbitrary small value.
261−
262− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
263− QPointF touchPoint = initialTouchPos;
264−
265− QPointF dragDirectionVector(1.0, 0.0);
266− QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
267−
268− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
269− QVERIFY(!fakeTimer->isRunning());
270−
271− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
272−
273− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
274− QVERIFY(fakeTimer->isRunning());
275−
276− // Move beyond distance threshold and composition time to ensure recognition
277− while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
278− (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
279−
280− QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
281−
282− touchPoint += touchMovement;
283− passTime(timeStepMs);
284− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
285− }
286−
287− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
288− QVERIFY(!fakeTimer->isRunning());
289−}
290−
291−/*
292− Checks that it informs the X coordinate of the touch point in local and scene coordinates
293− correctly.
294− */
295−void tst_DirectionalDragArea::sceneXAndX()
296−{
297− DirectionalDragArea *edgeDragArea =
298− m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
299− QVERIFY(edgeDragArea != 0);
300− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
301− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
302− edgeDragArea->setImmediateRecognition(true);
303−
304− QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
305−
306− sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
307−
308− QSignalSpy touchXSpy(edgeDragArea, &DirectionalDragArea::touchXChanged);
309− QSignalSpy touchSceneXSpy(edgeDragArea, &DirectionalDragArea::touchSceneXChanged);
310−
311− touchScenePos.rx() = m_view->width() / 2;
312− sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
313−
314− QCOMPARE(touchXSpy.count(), 1);
315− QCOMPARE(touchSceneXSpy.count(), 1);
316− QCOMPARE(edgeDragArea->touchX(), touchScenePos.x() - edgeDragArea->x());
317− QCOMPARE(edgeDragArea->touchSceneX(), touchScenePos.x());
318−}
319−
320−/*
321− Checks that it informs the Y coordinate of the touch point in local and scene coordinates
322− correctly.
323− */
324−void tst_DirectionalDragArea::sceneYAndY()
325−{
326− DirectionalDragArea *edgeDragArea =
327− m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
328− QVERIFY(edgeDragArea != 0);
329− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
330− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
331− edgeDragArea->setImmediateRecognition(true);
332−
333− QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
334−
335− sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
336−
337− QSignalSpy touchYSpy(edgeDragArea, &DirectionalDragArea::touchYChanged);
338− QSignalSpy touchSceneYSpy(edgeDragArea, &DirectionalDragArea::touchSceneYChanged);
339−
340− touchScenePos.ry() = m_view->height() / 2;
341− sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
342−
343− QCOMPARE(touchYSpy.count(), 1);
344− QCOMPARE(touchSceneYSpy.count(), 1);
345− QCOMPARE(edgeDragArea->touchY(), touchScenePos.y() - edgeDragArea->y());
346− QCOMPARE(edgeDragArea->touchSceneY(), touchScenePos.y());
347−}
348−
349−/*
350− Regression test for https://bugs.launchpad.net/bugs/1228336
351−
352− Perform a quick two-finger double-tap and check that DirectionalDragArea properly
353− rejects those touch points. In the bug above it got confused and crashed.
354− */
355−void tst_DirectionalDragArea::twoFingerTap()
356−{
357− DirectionalDragArea *edgeDragArea =
358− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
359− QVERIFY(edgeDragArea != 0);
360− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
361− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
362−
363− // Make touches evenly spaced along the edgeDragArea
364− QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
365− QPoint touchBPos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
366−
367− qint64 timeStepMsecs = 5; // some arbitrary, small value
368−
369− // Perform the first two-finger tap
370− // NB: using move() instead of stationary() becasue in the latter you cannot provide
371− // the touch position and therefore it's left with some garbage numbers.
372− QTest::touchEvent(m_view, m_device)
373− .press(0, touchAPos);
374−
375− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
376−
377− passTime(timeStepMsecs);
378− QTest::touchEvent(m_view, m_device)
379− .move(0, touchAPos)
380− .press(1, touchBPos);
381−
382− // A second touch point appeared during recognition, reject immediately as this
383− // can't be a single-touch gesture anymore.
384− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
385−
386− passTime(timeStepMsecs);
387− QTest::touchEvent(m_view, m_device)
388− .release(0, touchAPos)
389− .move(1, touchBPos);
390−
391− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
392−
393− passTime(timeStepMsecs);
394− QTest::touchEvent(m_view, m_device)
395− .release(1, touchBPos);
396−
397− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
398−
399− // Perform the second two-finger tap
400−
401− passTime(timeStepMsecs);
402− QTest::touchEvent(m_view, m_device)
403− .press(0, touchAPos);
404−
405− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
406−
407− passTime(timeStepMsecs);
408− QTest::touchEvent(m_view, m_device)
409− .move(0, touchAPos)
410− .press(1, touchBPos);
411−
412− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
413−
414− passTime(timeStepMsecs);
415− QTest::touchEvent(m_view, m_device)
416− .release(0, touchAPos)
417− .move(1, touchBPos);
418−
419− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
420−
421− passTime(timeStepMsecs);
422− QTest::touchEvent(m_view, m_device)
423− .release(1, touchBPos);
424−
425− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
426−}
427−
428−/*
429− Tests that gesture recognition works normally even if the DirectionalDragArea moves
430− during recognition.
431− This effectively means that we have to do gesture recognition with scene coordinates
432− instead of local item coordinates.
433− */
434−void tst_DirectionalDragArea::movingDDA()
435−{
436− QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
437− Q_ASSERT(rightwardsLauncher != 0);
438−
439− DirectionalDragArea *edgeDragArea =
440− rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
441− Q_ASSERT(edgeDragArea != 0);
442− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
443− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
444−
445− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
446− QPointF touchPoint = initialTouchPos;
447−
448− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
449− QPointF dragDirectionVector(1.0f, 0.0f);
450−
451− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
452− QPointF touchMovement = dragDirectionVector * movementStepDistance;
453− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
454− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
455−
456− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
457−
458− // Move it far ahead along the direction of the gesture
459− // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
460− rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
461−
462− for (int i = 0; i < totalMovementSteps; ++i) {
463− touchPoint += touchMovement;
464− passTime(movementTimeStepMs);
465− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
466− }
467−
468− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
469−
470− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
471−}
472−
473−/*
474− The presence of and old, rejected, touch point lying on the DirectionalDragArea
475− shouldn't impede the recognition of a gesture from a new touch point.
476− */
477−void tst_DirectionalDragArea::ignoreOldFinger()
478−{
479− DirectionalDragArea *edgeDragArea =
480− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
481− Q_ASSERT(edgeDragArea != 0);
482− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
483− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
484−
485− // Make touches evenly spaced along the edgeDragArea
486− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
487− QPoint touch1Pos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
488−
489− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
490−
491− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
492−
493− // leave it lying around for some time
494− passTime(edgeDragArea->d->maxTime * 10);
495−
496− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
497−
498− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
499− QPointF dragDirectionVector(1.0f, 0.0f);
500−
501− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
502− QPointF touchMovement = dragDirectionVector * movementStepDistance;
503− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
504− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
505−
506− QTest::touchEvent(m_view, m_device)
507− .move(0, touch0Pos)
508− .press(1, touch1Pos);
509−
510− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
511−
512− for (int i = 0; i < totalMovementSteps; ++i) {
513− touch1Pos += touchMovement.toPoint();
514− passTime(movementTimeStepMs);
515− QTest::touchEvent(m_view, m_device)
516− .move(0, touch0Pos)
517− .move(1, touch1Pos);
518− }
519−
520− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
521−
522− QTest::touchEvent(m_view, m_device)
523− .move(0, touch0Pos)
524− .release(1, touch1Pos);
525−
526− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
527−}
528−
529−/*
530− A Rightwards DDA that is rotated 90 degrees clockwise should recognize gestures
531− that are done downwards in scene coordinates. I.e. the gesture recognition direction
532− should be in local coordinates, not scene coordinates.
533− */
534−void tst_DirectionalDragArea::rotated()
535−{
536− QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
537− baseItem->setRotation(90.);
538−
539− QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
540− Q_ASSERT(rightwardsLauncher != 0);
541−
542− DirectionalDragArea *edgeDragArea =
543− rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
544− Q_ASSERT(edgeDragArea != 0);
545− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
546− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
547−
548− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
549− QPointF touchPoint = initialTouchPos;
550−
551− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
552− QPointF dragDirectionVector(0.0f, 1.0f);
553−
554− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
555− QPointF touchMovement = dragDirectionVector * movementStepDistance;
556− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
557− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
558−
559− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
560−
561− for (int i = 0; i < totalMovementSteps; ++i) {
562− touchPoint += touchMovement;
563− passTime(movementTimeStepMs);
564− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
565− }
566−
567− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
568−
569− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
570−}
571−
572−void tst_DirectionalDragArea::sceneDistance()
573−{
574− QQuickItem *baseItem = m_view->rootObject()->findChild<QQuickItem*>("baseItem");
575− QFETCH(qreal, rotation);
576− QFETCH(QPointF, dragDirectionVector);
577− baseItem->setRotation(rotation);
578−
579− QQuickItem *rightwardsLauncher = m_view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
580− Q_ASSERT(rightwardsLauncher != 0);
581−
582− DirectionalDragArea *edgeDragArea =
583− rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
584− Q_ASSERT(edgeDragArea != 0);
585− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
586− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
587−
588− // to disable the position smoothing so that we can more easily check sceneDistance values
589− edgeDragArea->setImmediateRecognition(true);
590−
591− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
592− QPointF touchPoint = initialTouchPos;
593−
594− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
595−
596− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
597− QPointF touchMovement = dragDirectionVector * movementStepDistance;
598− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
599− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
600−
601− qint64 timestamp = 0;
602−
603− sendTouchPress(timestamp, 0, touchPoint);
604−
605− for (int i = 0; i < totalMovementSteps; ++i) {
606− touchPoint += touchMovement;
607− timestamp += movementTimeStepMs;
608− sendTouchUpdate(timestamp, 0, touchPoint);
609− }
610−
611− qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
612−
613− // DirectionalDragArea::sceneDistance() must match the actual drag distance as the
614− // drag was aligned with the gesture direction
615− // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
616− QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
617−
618− timestamp += movementTimeStepMs;
619− sendTouchRelease(timestamp, 0, touchPoint);
620−}
621−
622−void tst_DirectionalDragArea::sceneDistance_data()
623−{
624− QTest::addColumn<qreal>("rotation");
625− QTest::addColumn<QPointF>("dragDirectionVector");
626−
627− QTest::newRow("not rotated") << 0. << QPointF(1., 0.);
628− QTest::newRow("rotated by 90 degrees") << 90. << QPointF(0., 1.);
629−}
630−
631−/*
632− Regression test for https://bugs.launchpad.net/unity8/+bug/1276122
633−
634− The bug:
635− If setting "enabled: false" while the DirectionalDragArea's (DDA) dragging
636− property is true, the DDA stays in that state and doesn't recover any more.
637−*/
638−void tst_DirectionalDragArea::disabledWhileDragging()
639−{
640− DirectionalDragArea *edgeDragArea =
641− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
642− Q_ASSERT(edgeDragArea != 0);
643− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
644− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
645−
646− QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
647−
648− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
649− QPointF dragDirectionVector(1., 0.); // horizontal positive
650−
651− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
652− QPointF touchMovement = dragDirectionVector * movementStepDistance;
653− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
654− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
655−
656− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
657−
658− for (int i = 0; i < totalMovementSteps; ++i) {
659− touchPoint += touchMovement;
660− passTime(movementTimeStepMs);
661− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
662− }
663−
664− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
665− QCOMPARE(edgeDragArea->dragging(), true);
666−
667− // disable the dragArea while it's being dragged.
668− edgeDragArea->setEnabled(false);
669−
670− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
671− QCOMPARE(edgeDragArea->dragging(), false);
672−
673− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
674−}
675−
676−void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
677−{
678− DirectionalDragArea *edgeDragArea =
679− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
680− Q_ASSERT(edgeDragArea != 0);
681− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
682− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
683−
684− // Disable some constraints we're not interested in
685− edgeDragArea->d->setMaxTime(60 * 1000);
686−
687− // And ensure others have the values we want
688− edgeDragArea->d->compositionTime = 60;
689−
690− // Put an item right behind edgeDragArea to receive the touches ignored by it
691− DummyItem *dummyItem = new DummyItem;
692− dummyItem->setParentItem(edgeDragArea->parentItem());
693− dummyItem->setX(edgeDragArea->x());
694− dummyItem->setY(edgeDragArea->y());
695− dummyItem->setZ(edgeDragArea->z() - 1.0);
696− dummyItem->setWidth(edgeDragArea->width());
697− dummyItem->setHeight(edgeDragArea->height());
698−
699− // Make touches evenly spaced along the edgeDragArea
700− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
701− QPoint touch1Pos(edgeDragArea->width()/2.0f, m_view->height()*0.66f);
702−
703− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
704−
705− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
706−
707− // We are now going to be way beyond compositionTime
708− passTime(edgeDragArea->d->compositionTime*3);
709−
710− QTest::touchEvent(m_view, m_device)
711− .move(0, touch0Pos)
712− .press(1, touch1Pos);
713−
714− // A new touch has come, but as it can't be composed with touch 0, it should be
715− // ignored/rejected by the DirectionalDragArea
716− // Therefore the last event received by dummyItem must have both touch points (0 and 1)
717− {
718− TouchMemento &touchMemento = dummyItem->touchEvents.last();
719− QCOMPARE(touchMemento.touchPoints.count(), 2);
720− QVERIFY(touchMemento.containsTouchWithId(0));
721− QVERIFY(touchMemento.containsTouchWithId(1));
722− }
723−
724− passTime(30);
725−
726− QTest::touchEvent(m_view, m_device)
727− .move(0, touch0Pos)
728− .move(1, touch1Pos);
729−
730− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
731−
732− passTime(5);
733−
734− QTest::touchEvent(m_view, m_device)
735− .release(0, touch0Pos)
736− .move(1, touch1Pos);
737−
738− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
739−
740− passTime(5);
741−
742− QTest::touchEvent(m_view, m_device)
743− .release(1, touch1Pos);
744−
745− // Shouldn't be keepping info about touches that no longer exist or interest us
746− QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
747−
748− delete dummyItem;
749−}
750−
751−void tst_DirectionalDragArea::givesUpWhenLosesTouch()
752−{
753− DirectionalDragArea *edgeDragArea =
754− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
755− Q_ASSERT(edgeDragArea != 0);
756− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
757− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
758−
759− // Disable some constraints we're not interested in
760− edgeDragArea->d->setMaxTime(60 * 1000);
761−
762− // Put an item right in front of edgeDragArea
763− DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
764− dummyItem->setX(edgeDragArea->x());
765− dummyItem->setY(edgeDragArea->y());
766− dummyItem->setZ(edgeDragArea->z() + 1.0);
767− dummyItem->setWidth(edgeDragArea->width());
768− dummyItem->setHeight(edgeDragArea->height());
769−
770− QPoint touchPos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
771−
772− dummyItem->touchEventHandler = [&](QTouchEvent *event) {
773− m_touchRegistry->addCandidateOwnerForTouch(0, dummyItem);
774− event->ignore();
775− };
776−
777− QTest::touchEvent(m_view, m_device).press(0, touchPos);
778−
779− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
780−
781− m_touchRegistry->requestTouchOwnership(0, dummyItem);
782−
783− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
784−
785− dummyItem->grabTouchPoints({0});
786− dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
787−
788− passTime(5);
789− QTest::touchEvent(m_view, m_device).release(0, touchPos);
790−
791− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
792−
793− QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
794−}
795−
796−void tst_DirectionalDragArea::threeFingerDrag()
797−{
798− DirectionalDragArea *edgeDragArea =
799− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
800− Q_ASSERT(edgeDragArea != 0);
801− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
802− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
803−
804− // Disable some constraints we're not interested in
805− edgeDragArea->d->setMaxTime(60 * 1000);
806−
807− // And ensure others have the values we want
808− edgeDragArea->d->compositionTime = 60;
809−
810− // Make touches evenly spaced along the edgeDragArea
811− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
812− QPoint touch1Pos(edgeDragArea->width()/2.0f, m_view->height()*0.50f);
813− QPoint touch2Pos(edgeDragArea->width()/2.0f, m_view->height()*0.75f);
814−
815− QTest::touchEvent(m_view, m_device)
816− .press(0, touch0Pos);
817−
818− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
819−
820− passTime(5);
821− QTest::touchEvent(m_view, m_device)
822− .move(0, touch0Pos)
823− .press(1, touch1Pos);
824−
825− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
826−
827− passTime(5);
828− QTest::touchEvent(m_view, m_device)
829− .move(0, touch0Pos)
830− .move(1, touch1Pos)
831− .press(2, touch2Pos);
832−
833− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
834−
835− passTime(10);
836− QTest::touchEvent(m_view, m_device)
837− .move(0, touch0Pos)
838− .move(1, touch1Pos)
839− .move(2, touch2Pos);
840−
841− passTime(200);
842− QTest::touchEvent(m_view, m_device)
843− .move(0, touch0Pos)
844− .release(1, touch1Pos)
845− .move(2, touch2Pos);
846−
847− passTime(10);
848− QTest::touchEvent(m_view, m_device)
849− .move(0, touch0Pos)
850− .release(2, touch2Pos);
851−
852− passTime(5);
853− QTest::touchEvent(m_view, m_device)
854− .release(0, touch0Pos);
855−
856− // Shouldn't be keepping info about touches that no longer exist or interest us
857− QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
858−}
859−
860−/*
861− If all the relevant gesture recognition constraints/parameters have been disabled,
862− it means that the gesture recognition itself has been disabled and DirectionalDragArea
863− will therefore work like a simple touch area, merely reporting touch movement.
864− */
865−void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
866−{
867− DirectionalDragArea *edgeDragArea =
868− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
869− Q_ASSERT(edgeDragArea != 0);
870− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
871− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
872−
873− // Disable the minimum amount of constraints to ensure immediate recognition
874− edgeDragArea->d->setDistanceThreshold(0);
875− edgeDragArea->d->compositionTime = 0;
876−
877− // Put an item right behind edgeDragArea to receive the touches ignored by it
878− DummyItem *dummyItem = new DummyItem;
879− dummyItem->setParentItem(edgeDragArea->parentItem());
880− dummyItem->setX(edgeDragArea->x());
881− dummyItem->setY(edgeDragArea->y());
882− dummyItem->setZ(edgeDragArea->z() - 1.0);
883− dummyItem->setWidth(edgeDragArea->width());
884− dummyItem->setHeight(edgeDragArea->height());
885−
886− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
887−
888− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
889−
890− // check for immediate recognition
891− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
892−
893− // and therefore it should have immediately grabbed the touch point,
894− // not letting it leak to items behind him.
895− QCOMPARE(dummyItem->touchEvents.count(), 0);
896−}
897−
898−void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition()
899−{
900− DirectionalDragArea *edgeDragArea =
901− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
902− Q_ASSERT(edgeDragArea != 0);
903− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
904− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
905−
906− QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
907−
908− // Move less than the minimum needed for the drag gesture recognition
909− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
910− QPointF dragDirectionVector(1., 0.); // horizontal positive
911−
912− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
913− QPointF touchMovement = dragDirectionVector * movementStepDistance;
914− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
915− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
916−
917− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
918−
919− for (int i = 0; i < totalMovementSteps; ++i) {
920− touchPoint += touchMovement;
921− passTime(movementTimeStepMs);
922− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
923− }
924−
925− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
926−
927− // edgeDragArea should be an undecided candidate
928− {
929− auto touchInfo = m_touchRegistry->findTouchInfo(0);
930− QCOMPARE(touchInfo->candidates.size(), 1);
931− QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
932− QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
933− }
934−
935− // disable the dragArea while it's still recognizing a possible drag gesture.
936− QFETCH(bool, disable);
937− if (disable) {
938− edgeDragArea->setEnabled(false);
939− } else {
940− edgeDragArea->setVisible(false);
941− }
942−
943− // edgeDragArea should no longer be a candidate
944− {
945− auto touchInfo = m_touchRegistry->findTouchInfo(0);
946− QCOMPARE(touchInfo->candidates.size(), 0);
947− }
948−
949− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
950−
951− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
952−}
953−
954−void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data()
955−{
956− QTest::addColumn<bool>("disable");
957−
958− QTest::newRow("disabled") << true;
959− QTest::newRow("invisible") << false;
960−}
961−
962−void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
963−{
964− DirectionalDragArea *edgeDragArea =
965− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
966− QVERIFY(edgeDragArea != nullptr);
967− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
968− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
969−
970− QQuickMouseArea *mouseArea =
971− m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
972− QVERIFY(mouseArea != nullptr);
973−
974− MouseAreaSpy mouseAreaSpy(mouseArea);
975−
976− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
977− QPointF touchPoint = initialTouchPos;
978−
979− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
980− QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
981− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
982− QPointF touchMovement = dragDirectionVector * movementStepDistance;
983− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
984− int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
985−
986− QCOMPARE(mouseArea->pressed(), false);
987−
988− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
989−
990− // The TouchBegin passes through the DirectionalDragArea and reaches the MouseArea behind it,
991− // where it's converted to a MouseEvent by QQuickWindow and sent to the MouseArea which then
992− // accepts it. Making it get pressed.
993− QCOMPARE(mouseArea->pressed(), true);
994− QCOMPARE(mouseAreaSpy.canceledCount, 0);
995−
996− for (int i = 0; i < totalMovementSteps; ++i) {
997− touchPoint += touchMovement;
998− passTime(movementTimeStepMs);
999− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1000− }
1001−
1002− // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
1003− // which should make the MouseArea get a cancelation event, which will then cause it to
1004− // reset its state (going back to "unpressed"/"released").
1005− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1006− QCOMPARE(mouseArea->pressed(), false);
1007− QCOMPARE(mouseAreaSpy.canceledCount, 1);
1008−
1009− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1010−}
1011−
1012−void tst_DirectionalDragArea::interleavedTouches()
1013−{
1014− DirectionalDragArea *edgeDragArea =
1015− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1016− QVERIFY(edgeDragArea != 0);
1017− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1018− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1019−
1020− QPointF touch0 = edgeDragArea->mapToScene(
1021− QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.3));
1022−
1023− qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1024− QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1025− qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1026− QPointF touchMovement = dragDirectionVector * movementStepDistance;
1027− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1028− int movementTimeStepMs = (edgeDragArea->d->maxTime * 0.4f) / totalMovementSteps;
1029−
1030− QTest::touchEvent(m_view, m_device).press(0, touch0.toPoint());
1031− for (int i = 0; i < totalMovementSteps; ++i) {
1032− touch0 += touchMovement;
1033− passTime(movementTimeStepMs);
1034− QTest::touchEvent(m_view, m_device).move(0, touch0.toPoint());
1035− }
1036− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1037−
1038− QPointF touch1 = edgeDragArea->mapToScene(
1039− QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.6));
1040−
1041− QTest::touchEvent(m_view, m_device)
1042− .move(0, touch0.toPoint())
1043− .press(1, touch1.toPoint());
1044−
1045− touch1 += touchMovement;
1046− passTime(movementTimeStepMs);
1047− QTest::touchEvent(m_view, m_device)
1048− .move(0, touch0.toPoint())
1049− .move(1, touch1.toPoint());
1050−
1051− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1052−
1053− QTest::touchEvent(m_view, m_device)
1054− .release(0, touch0.toPoint())
1055− .move(1, touch1.toPoint());
1056−
1057− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1058−
1059− touch1 += touchMovement;
1060− passTime(movementTimeStepMs);
1061− QTest::touchEvent(m_view, m_device)
1062− .move(1, touch1.toPoint());
1063−
1064− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1065−
1066− QPointF touch2 = edgeDragArea->mapToScene(
1067− QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.9));
1068−
1069− passTime(edgeDragArea->d->compositionTime + movementTimeStepMs);
1070− QTest::touchEvent(m_view, m_device)
1071− .move(1, touch1.toPoint())
1072− .press(2, touch2.toPoint());
1073−
1074− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1075− QCOMPARE(edgeDragArea->d->touchId, 2);
1076−
1077− touch2 += touchMovement;
1078− passTime(movementTimeStepMs);
1079− QTest::touchEvent(m_view, m_device)
1080− .move(1, touch1.toPoint())
1081− .move(2, touch2.toPoint());
1082−
1083− touch1 += touchMovement;
1084− passTime(movementTimeStepMs);
1085− QTest::touchEvent(m_view, m_device)
1086− .move(1, touch1.toPoint())
1087− .move(2, touch2.toPoint());
1088−
1089− passTime(movementTimeStepMs);
1090− QTest::touchEvent(m_view, m_device)
1091− .release(1, touch1.toPoint())
1092− .move(2, touch2.toPoint());
1093−
1094− passTime(movementTimeStepMs);
1095− QTest::touchEvent(m_view, m_device)
1096− .release(2, touch2.toPoint());
1097−
1098− QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1099−}
1100−
1101−/*
1102− A valid right-edge drag performed on mako
1103− */
1104−void tst_DirectionalDragArea::makoRightEdgeDrag()
1105−{
1106− m_view->resize(768, 1280);
1107− QTest::qWait(300);
1108−
1109− DirectionalDragArea *edgeDragArea =
1110− m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1111− QVERIFY(edgeDragArea != nullptr);
1112− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1113− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1114−
1115− StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1116−
1117− edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1118−
1119− sendTouchPress(319744, 0, QPointF(767.001, 719.719));
1120− sendTouchUpdate(319765, 0, QPointF(765.744,729.973));
1121− sendTouchUpdate(319784, 0, QPointF(740.879,752.182));
1122− sendTouchUpdate(319803, 0, QPointF(689.698,795.795));
1123− sendTouchUpdate(319826, 0, QPointF(616.978,856.212));
1124− sendTouchUpdate(319845, 0, QPointF(558.769,906.157));
1125− sendTouchUpdate(319859, 0, QPointF(513.219,945.266));
1126− sendTouchUpdate(319878, 0, QPointF(481.31,975.496));
1127− sendTouchUpdate(319902, 0, QPointF(460.016,997.439));
1128− sendTouchUpdate(319920, 0, QPointF(449.761,1008.6));
1129− sendTouchUpdate(319929, 0, QPointF(445.891,1012.42));
1130− sendTouchUpdate(319947, 0, QPointF(444.884,1013.93));
1131− sendTouchUpdate(319965, 0, QPointF(444.461,1014.35));
1132− sendTouchUpdate(320057, 0, QPointF(444.71,1013.56));
1133− sendTouchUpdate(320138, 0, QPointF(445.434,1013.6));
1134− sendTouchUpdate(320154, 0, QPointF(446.338,1012.98));
1135− sendTouchUpdate(320171, 0, QPointF(447.232,1012.08));
1136− sendTouchRelease(320171, 0, QPointF(447.232,1012.08));
1137−
1138− QCOMPARE(statusSpy->recognized(), true);
1139−
1140− delete statusSpy;
1141−}
1142−
1143−/*
1144− A vertical, downwards swipe performed on mako near its right edge.
1145−
1146− The DirectionalDragArea on the right edge must not recognize this
1147− gesture.
1148− */
1149−void tst_DirectionalDragArea::makoRightEdgeDrag_verticalDownwards()
1150−{
1151− m_view->resize(768, 1280);
1152− QTest::qWait(300);
1153−
1154− DirectionalDragArea *edgeDragArea =
1155− m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1156− QVERIFY(edgeDragArea != nullptr);
1157− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1158− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1159−
1160− edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1161−
1162− StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1163−
1164− sendTouchPress(12012445, 26, QPointF(767.001,461.82));
1165− sendTouchUpdate(12012472, 26, QPointF(767.001,462.569));
1166− sendTouchUpdate(12012528, 26, QPointF(767.001,463.334));
1167− sendTouchUpdate(12012546, 26, QPointF(767.001,466.856));
1168− sendTouchUpdate(12012571, 26, QPointF(767.001,473.291));
1169− sendTouchUpdate(12012587, 26, QPointF(767.001,487.31));
1170− sendTouchUpdate(12012604, 26, QPointF(765.364,507.521));
1171− sendTouchUpdate(12012618, 26, QPointF(765.364,507.521));
1172− sendTouchUpdate(12012627, 26, QPointF(762.642,534.317));
1173− sendTouchUpdate(12012655, 26, QPointF(760.846,573.406));
1174− sendTouchUpdate(12012667, 26, QPointF(759.838,625.295));
1175− sendTouchUpdate(12012675, 26, QPointF(758.875,703.207));
1176− sendTouchUpdate(12012696, 26, QPointF(761.52,777.015));
1177− sendTouchUpdate(12012713, 26, QPointF(765.659,835.591));
1178− sendTouchUpdate(12012731, 26, QPointF(766.778,883.206));
1179− sendTouchUpdate(12012748, 26, QPointF(767.001,922.937));
1180− sendTouchUpdate(12012779, 26, QPointF(767.001,967.558));
1181− sendTouchUpdate(12012798, 26, QPointF(767.001,1006.12));
1182− sendTouchUpdate(12012809, 26, QPointF(767.001,1033.1));
1183− sendTouchRelease(12012810, 26, QPointF(767.001,1033.1));
1184−
1185− QCOMPARE(statusSpy->recognized(), false);
1186−
1187− delete statusSpy;
1188−}
1189−
1190−/*
1191− A valid left-edge drag performed on mako. This one starts a bit slow than speeds up
1192− */
1193−void tst_DirectionalDragArea::makoLeftEdgeDrag_slowStart()
1194−{
1195− m_view->resize(768, 1280);
1196− QTest::qWait(300);
1197−
1198− DirectionalDragArea *edgeDragArea =
1199− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1200− QVERIFY(edgeDragArea != nullptr);
1201− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1202− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1203−
1204− edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1205−
1206− StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1207−
1208− sendTouchPress(4002267, 77, QPointF(0,885.154));
1209− sendTouchUpdate(4002275, 77, QPointF(0,886.214));
1210− sendTouchUpdate(4002311, 77, QPointF(1.09568,887.75));
1211− sendTouchUpdate(4002329, 77, QPointF(3.53647,890.191));
1212− sendTouchUpdate(4002347, 77, QPointF(7.87434,892.879));
1213− sendTouchUpdate(4002366, 77, QPointF(12.3036,895.075));
1214− sendTouchUpdate(4002384, 77, QPointF(15.8885,896.849));
1215− sendTouchUpdate(4002406, 77, QPointF(18.4504,897.88));
1216− sendTouchUpdate(4002420, 77, QPointF(20.2429,898.149));
1217− sendTouchUpdate(4002439, 77, QPointF(20.9945,898.149));
1218− sendTouchUpdate(4002457, 77, QPointF(21.8819,898.149));
1219− sendTouchUpdate(4002480, 77, QPointF(22.7454,897.389));
1220− sendTouchUpdate(4002493, 77, QPointF(23.5456,896.589));
1221− sendTouchUpdate(4002511, 77, QPointF(24.5435,895.031));
1222− sendTouchUpdate(4002529, 77, QPointF(25.4271,892.32));
1223− sendTouchUpdate(4002548, 77, QPointF(26.3145,889.658));
1224− sendTouchUpdate(4002566, 77, QPointF(27.2004,886.999));
1225− sendTouchUpdate(4002584, 77, QPointF(28.035,885.048));
1226− sendTouchUpdate(4002603, 77, QPointF(29.9684,883.167));
1227− sendTouchUpdate(4002620, 77, QPointF(33.3591,881.403));
1228− sendTouchUpdate(4002639, 77, QPointF(44.1017,879.642));
1229− sendTouchUpdate(4002657, 77, QPointF(64.828,878.502));
1230− sendTouchUpdate(4002675, 77, QPointF(87.9486,878.157));
1231− sendTouchUpdate(4002693, 77, QPointF(112.96,877.742));
1232− sendTouchUpdate(4002711, 77, QPointF(138.903,877.157));
1233− sendTouchUpdate(4002729, 77, QPointF(163.204,877.157));
1234− sendTouchUpdate(4002747, 77, QPointF(182.127,877.157));
1235− sendTouchUpdate(4002765, 77, QPointF(194.478,877.657));
1236− sendTouchUpdate(4002785, 77, QPointF(201.474,878.508));
1237− sendTouchUpdate(4002803, 77, QPointF(204.855,879.401));
1238− sendTouchUpdate(4002822, 77, QPointF(206.616,880.281));
1239− sendTouchUpdate(4002839, 77, QPointF(207.115,880.906));
1240− sendTouchUpdate(4002894, 77, QPointF(206.865,881.184));
1241− sendTouchUpdate(4002912, 77, QPointF(206.865,882.143));
1242− sendTouchUpdate(4002930, 77, QPointF(206.865,883.106));
1243− sendTouchUpdate(4002949, 77, QPointF(206.526,883.994));
1244− sendTouchUpdate(4002967, 77, QPointF(205.866,884.88));
1245− sendTouchUpdate(4002985, 77, QPointF(205.866,885.766));
1246− sendTouchUpdate(4003005, 77, QPointF(205.866,886.654));
1247− sendTouchUpdate(4003021, 77, QPointF(205.366,887.537));
1248− sendTouchUpdate(4003039, 77, QPointF(204.592,888.428));
1249− sendTouchUpdate(4003050, 77, QPointF(204.367,888.653));
1250− sendTouchRelease(4003050, 77, QPointF(204.367,888.653));
1251−
1252− QCOMPARE(statusSpy->recognized(), true);
1253−
1254− delete statusSpy;
1255−}
1256−
1257−void tst_DirectionalDragArea::makoLeftEdgeDrag_movesSlightlyBackwardsOnStart()
1258−{
1259− m_view->resize(768, 1280);
1260− QTest::qWait(300);
1261−
1262− DirectionalDragArea *edgeDragArea =
1263− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1264− QVERIFY(edgeDragArea != nullptr);
1265− edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1266− edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1267−
1268− edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1269−
1270− StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1271−
1272− sendTouchPress(41097, 24, QPointF(13.9909,827.177));
1273− sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
1274− sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
1275− sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
1276− sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
1277− sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
1278− sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
1279− sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
1280− sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
1281− sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
1282− sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
1283− sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
1284− sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
1285− sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
1286− sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
1287− sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
1288− sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
1289− sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
1290− sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
1291− sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
1292− sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
1293− sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
1294− sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
1295− sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
1296− sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
1297− sendTouchRelease(41607, 24, QPointF(183.88,793.69));
1298−
1299− QCOMPARE(statusSpy->recognized(), true);
1300−
1301− delete statusSpy;
1302−}
1303−
1304−QTEST_MAIN(tst_DirectionalDragArea)
1305−
1306−#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+1−1
@@ -25,7 +25,7 @@
2525#include <functional>
2626
2727#include <TouchGate.h>
28−#include <TouchRegistry.h>
28+#include <TouchRegistry>
2929
3030#include "TestItem.h"
3131
tests/qmltests/Components/CMakeLists.txt+10−7
@@ -1,25 +1,28 @@
11add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
22
3+pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
4+
35########## tst_DragHandle
46
57include_directories(
68 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
7− ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
9+ ${UBUNTUGESTURES_INCLUDE_DIRS}
810 ${CMAKE_CURRENT_BINARY_DIR}
911 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1012 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1113 )
1214
13−add_executable(DragHandleTestExec tst_DragHandle.cpp)
14−qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
15−target_link_libraries(DragHandleTestExec UbuntuGesturesQml UbuntuGestures)
15+# TODO
16+# add_executable(DragHandleTestExec tst_DragHandle.cpp)
17+# qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
18+# target_link_libraries(DragHandleTestExec UbuntuGesturesQml ${UBUNTUGESTURES_LIBRARIES})
1619
17−add_unity8_uitest(DragHandle DragHandleTestExec ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures)
18−add_manual_qml_test(. DragHandle IMPORT_PATHS ${UNITY_PLUGINPATH})
20+# add_unity8_uitest(DragHandle DragHandleTestExec ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures)
21+# add_manual_qml_test(. DragHandle IMPORT_PATHS ${UNITY_PLUGINPATH})
1922
2023########## tst_EdgeDragEvaluator
2124add_executable(EdgeDragEvaluatorTestExec tst_EdgeDragEvaluator.cpp)
2225qt5_use_modules(EdgeDragEvaluatorTestExec Test Core Qml Gui Quick)
23−target_link_libraries(EdgeDragEvaluatorTestExec UbuntuGesturesQml UbuntuGestures)
26+target_link_libraries(EdgeDragEvaluatorTestExec UbuntuGesturesQml ${UBUNTUGESTURES_LIBRARIES})
2427
2528add_unity8_uitest(EdgeDragEvaluator EdgeDragEvaluatorTestExec ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures)
tests/qmltests/Components/tst_DragHandle.cpp+1−3
@@ -23,9 +23,7 @@
2323#include <private/qquickwindow_p.h>
2424
2525#include <AxisVelocityCalculator.h>
26−#include <DirectionalDragArea.h>
27−#include <DirectionalDragArea_p.h>
28−#include <Timer.h>
26+#include <Timer>
2927
3028using namespace UbuntuGestures;
3129
tests/qmltests/Components/tst_EdgeDragEvaluator.cpp+1−1
@@ -24,7 +24,7 @@
2424// Ubuntu.Gestures plugin
2525#include <AxisVelocityCalculator.h>
2626#include <Direction.h>
27−#include <Timer.h>
27+#include <Timer>
2828
2929using namespace UbuntuGestures;
3030
tests/qmltests/Dash/tst_Dash.qml+7
@@ -303,25 +303,32 @@ Item {
303303 }
304304
305305 function test_manage_dash_store_no_favorites() {
306+ console.log("A");
306307 // Show the manage dash
307308 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
308309 var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
309310 tryCompare(bottomEdgeController, "progress", 1);
310311
312+ console.log("B");
311313 // clear the favorite scopes
312314 scopes.clearFavorites();
313315 var dashContentList = findChild(dash, "dashContentList");
314316 tryCompare(dashContentList, "count", 0);
315317
318+ console.log("C");
316319 var scopesList = findChild(dash, "scopesList");
317320 spy.target = scopesList.scope;
318321 spy.signalName = "queryPerformed";
319322
323+ console.log("D");
320324 // Click on the store
321325 var scopesListPageHeader = findChild(scopesList, "pageHeader");
322326 var searchButton = findChild(scopesListPageHeader, "store_action_button");
327+ console.log("D", searchButton, scopesListPageHeader);
328+ wait(1000);
323329 mouseClick(searchButton);
324330
331+ console.log("E");
325332 spy.wait();
326333 compare(spy.signalArguments[0][0], "scope://com.canonical.scopes.clickstore");
327334 tryCompare(bottomEdgeController, "progress", 0);
tests/qmltests/Stages/tst_PhoneStage.qml+3
@@ -139,9 +139,12 @@ Item {
139139
140140 var activeApp = ApplicationManager.get(0);
141141 var inactiveApp = ApplicationManager.get(1);
142+ wait(1000);
142143
144+ console.log("STARTING FLICK");
143145 touchFlick(phoneStage, startX, startY, endX, endY,
144146 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
147+ console.log("FLICK DONE");
145148
146149 tryCompare(ApplicationManager, "focusedApplicationId", inactiveApp.appId)
147150
tests/utils/modules/Unity/Test/CMakeLists.txt+5−2
@@ -2,10 +2,12 @@ include(QmlPlugins)
22
33add_definitions(-DQT_NO_KEYWORDS)
44
5+pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
6+
57include_directories(
68 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
79 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
8− ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
10+ ${UBUNTUGESTURES_INCLUDE_DIRS}
911 ${CMAKE_SOURCE_DIR}/src
1012)
1113
@@ -16,8 +18,9 @@ set(UnityTestQML_SOURCES
1618 ${CMAKE_SOURCE_DIR}/src/MouseTouchAdaptor.cpp
1719)
1820
21+
1922add_library(UnityTestQml MODULE ${UnityTestQML_SOURCES})
20−target_link_libraries(UnityTestQml UbuntuGestures)
23+target_link_libraries(UnityTestQml ${UBUNTUGESTURES_LIBRARIES})
2124
2225qt5_use_modules(UnityTestQml Core Quick Test)
2326
tests/utils/modules/Unity/Test/testutil.cpp+2−2
@@ -22,8 +22,8 @@
2222#include <QQuickView>
2323
2424// UbuntuGestures lib
25−#include <TouchRegistry.h>
26−#include <Timer.h>
25+#include <TouchRegistry>
26+#include <Timer>
2727
2828using namespace UbuntuGestures;
2929