2
* Copyright (C) 2013,2014 Canonical, Ltd.
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.
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.
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/>.
17
#ifndef DIRECTIONAL_DRAG_AREA_H
18
#define DIRECTIONAL_DRAG_AREA_H
20
#include <QtQuick/QQuickItem>
21
#include "AxisVelocityCalculator.h"
22
#include "UbuntuGesturesQmlGlobal.h"
24
#include "Direction.h"
30
class TouchOwnershipEvent;
31
class UnownedTouchEvent;
34
An area that detects axis-aligned single-finger drag gestures
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
40
See doc/DirectionalDragArea.svg
42
class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
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)
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)
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)
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)
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)
64
// The current status of the directional drag gesture area.
65
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
67
// Whether a drag gesture is taking place
68
// This will be true as long as status is Undecided or Recognized
69
// When a gesture gets rejected, dragging turns to false.
70
Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
73
// stuff that will be set in stone at some point
75
// How far the touch point can move away from its expected position before
76
// it causes a rejection in the gesture recognition. This is to compensate
77
// for both noise in the touch input signal and for the natural irregularities
78
// in the finger movement.
79
// Proper value is likely device-specific.
80
Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
82
// Widening angle, in degrees
83
// It's roughly the maximum angle a touch point can make relative to the
84
// axis defined by the compoment's direction for it to be recognized as a
86
Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
87
NOTIFY wideningAngleChanged)
89
// How far a touch point has to move from its initial position in order for
90
// it to be recognized as a directional drag.
91
Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
92
NOTIFY distanceThresholdChanged)
94
// Minimum speed a gesture needs to have in order to be recognized as a
96
// In pixels per second
97
Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
99
// A gesture will be rejected if more than maxSilenceTime milliseconds has
100
// passed since we last got an input event from it (during Undecided state).
102
// Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
103
// finger is still (zero drag speed). In some cases the finger might be moving but
104
// the driver's high noise filtering might cause those silence periods, specially
105
// in the moments succeeding a press (talking about Galaxy Nexus here).
106
Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
107
WRITE setMaxSilenceTime
108
NOTIFY maxSilenceTimeChanged)
113
// Maximum time (in milliseconds) after the start of a given touch point where
114
// subsequent touch starts are grouped with the first one into an N-touches gesture
115
// (e.g. a two-fingers tap or drag).
116
Q_PROPERTY(int compositionTime READ compositionTime
117
WRITE setCompositionTime
118
NOTIFY compositionTimeChanged)
123
DirectionalDragArea(QQuickItem *parent = 0);
125
Direction::Type direction() const;
126
void setDirection(Direction::Type);
128
// Describes the state of the directional drag gesture.
130
// Waiting for a new touch point to land on this area. No gesture is being processed
134
// A touch point has landed on this area but it's not know yet whether it is
135
// performing a drag in the correct direction.
136
// If it's decided that the touch point is not performing a directional drag gesture,
137
// it will be rejected/ignored and status will return to WaitingForTouch.
138
Undecided, //Recognizing,
140
// There's a touch point in this area and it performed a drag in the correct
143
// Once recognized, the gesture state will move back to WaitingForTouch only once
144
// that touch point ends. The gesture will remain in the Recognized state even if
145
// the touch point starts moving in other directions or halts.
148
Status status() const { return m_status; }
150
qreal distance() const;
151
qreal sceneDistance() const;
152
void updateSceneDistance();
154
qreal touchX() const;
155
qreal touchY() const;
157
qreal touchSceneX() const;
158
qreal touchSceneY() const;
160
bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
162
qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
163
void setMaxDeviation(qreal value);
165
qreal wideningAngle() const;
166
void setWideningAngle(qreal value);
168
qreal distanceThreshold() const { return m_distanceThreshold; }
169
void setDistanceThreshold(qreal value);
171
qreal minSpeed() const { return m_minSpeed; }
172
void setMinSpeed(qreal value);
174
int maxSilenceTime() const { return m_maxSilenceTime; }
175
void setMaxSilenceTime(int value);
177
int compositionTime() const { return m_compositionTime; }
178
void setCompositionTime(int value);
180
// Replaces the existing Timer with the given one.
182
// Useful for providing a fake timer when testing.
183
void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
185
// Useful for testing, where a fake time source can be supplied
186
void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
188
bool event(QEvent *e) override;
190
// Maximum time, in milliseconds, between a press and a release, for a touch
191
// sequence to be considered a tap.
192
int maxTapDuration() const { return 300; }
195
void directionChanged(Direction::Type direction);
196
void statusChanged(Status value);
197
void draggingChanged(bool value);
198
void distanceChanged(qreal value);
199
void sceneDistanceChanged(qreal value);
200
void maxDeviationChanged(qreal value);
201
void wideningAngleChanged(qreal value);
202
void distanceThresholdChanged(qreal value);
203
void minSpeedChanged(qreal value);
204
void maxSilenceTimeChanged(int value);
205
void compositionTimeChanged(int value);
206
void touchXChanged(qreal value);
207
void touchYChanged(qreal value);
208
void touchSceneXChanged(qreal value);
209
void touchSceneYChanged(qreal value);
211
// TODO: I would rather not have such signal as it has nothing to do with drag gestures.
212
// Remove when no longer used or move its implementation to the QML code that uses it
213
// See maxTapDuration()
217
virtual void touchEvent(QTouchEvent *event);
221
void giveUpIfDisabledOrInvisible();
224
void touchEvent_absent(QTouchEvent *event);
225
void touchEvent_undecided(QTouchEvent *event);
226
void touchEvent_recognized(QTouchEvent *event);
227
bool pointInsideAllowedArea() const;
228
bool movingInRightDirection() const;
229
bool movedFarEnough(const QPointF &point) const;
230
const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
231
void setStatus(Status newStatus);
232
void setPreviousPos(const QPointF &point);
233
void setPreviousScenePos(const QPointF &point);
234
void updateVelocityCalculator(const QPointF &point);
235
bool isWithinTouchCompositionWindow();
236
void updateSceneDirectionVector();
237
// returns the scalar projection between the given vector (in scene coordinates)
238
// and m_sceneDirectionVector
239
qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
240
void touchOwnershipEvent(TouchOwnershipEvent *event);
241
void unownedTouchEvent(UnownedTouchEvent *event);
242
void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
243
void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
244
bool recognitionIsDisabled() const;
245
void emitSignalIfTapped();
250
QPointF m_startScenePos;
251
QPointF m_previousPos;
252
QPointF m_previousScenePos;
253
qreal m_sceneDistance;
256
// A movement damper is used in some of the gesture recognition calculations
257
// to get rid of noise or small oscillations in the touch position.
258
DampedPointF m_dampedScenePos;
259
QPointF m_previousDampedScenePos;
261
// Unit vector in scene coordinates describing the direction of the gesture recognition
262
QPointF m_sceneDirectionVector;
264
Direction::Type m_direction;
265
qreal m_wideningAngle; // in degrees
266
qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
267
qreal m_distanceThreshold;
268
qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
270
int m_maxSilenceTime; // in milliseconds
271
int m_silenceTime; // in milliseconds
272
int m_compositionTime; // in milliseconds
273
int m_numSamplesOnLastSpeedCheck;
274
UbuntuGestures::AbstractTimer *m_recognitionTimer;
275
AxisVelocityCalculator *m_velocityCalculator;
277
UbuntuGestures::SharedTimeSource m_timeSource;
279
// Information about an active touch point
280
struct ActiveTouchInfo {
281
ActiveTouchInfo() : id(-1), startTime(-1) {}
282
bool isValid() const { return id != -1; }
283
void reset() { id = -1; }
287
class ActiveTouchesInfo {
289
ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
290
void update(QTouchEvent *event);
291
qint64 touchStartTime(int id);
292
bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
293
qint64 mostRecentStartTime();
294
UbuntuGestures::SharedTimeSource m_timeSource;
296
void addTouchPoint(int touchId);
297
void removeTouchPoint(int touchId);
298
#if ACTIVETOUCHESINFO_DEBUG
302
Pool<ActiveTouchInfo> m_touchInfoPool;
305
friend class tst_DirectionalDragArea;
308
#endif // DIRECTIONAL_DRAG_AREA_H